HNOI2006 潘多拉的盒子
题解:
题目的描述比较长,理解起来也有一定难度。仔细读题后我们发现整个任务可以分成两个部分:找出咒语机之间所有的升级关系、求最长升级序列。
1、 求升级关系:
容易看出,咒语机i可以抽象成一个图Gi,其顶点集Vi为ni个元件,每个顶点发出两条边——“0”边和“1”边,分别表示将信号加“0”和加“1”。
我们枚举两个咒语机A、B(A≠B),判断B是否是A的升级。最简单的想法是生成出A和B的所有咒语源,然后判断前者是否为后者的子集。但是,一个咒语机产生的咒语源可能有无限多个,无法逐一判断。
其实,只要存在一个咒语源,A能够产生而B不能产生,那么这一升级关系就不成立。为了找到(或者证明不存在)这样的一个咒语源,我们构造图H,其顶点是一个二元组(i,j),表示图GA的顶点i和图GB的顶点j。如果图GA的顶点i走“c”边到达顶点ic(c=0,1),图GB的顶点j走“c”边到达顶点jc,那么从图H的顶点(i,j)连有向边到(ic,jc)。我们将图H中的某些顶点(i,j)称为“关键顶点”,其特点是:图GA的顶点i是输出元而图GB的顶点j不是输出元。存在一个A能够产生而B不能的咒语源,等价于图H中存在从顶点(0,0)到关键顶点的路径。我们只需用广度优先搜索遍历图H即可。
2、 求最长升级序列:
假设第1部分求出的升级关系保存在图G中:如果咒语机B是A的升级,那么图G中从A向B连一条有向边。最长升级序列在图中对应最长路经。如果G是有向无环图,那么可以用拓扑排序加上动态规划的方法求最长路径。可惜的是,G有可能存在环,并且只有一种可能:存在若干个咒语机,它们产生的咒语源完全相同。显然,在这种情况下,只要选择其中一个,那么与之相同的所有咒语机都可以被选择。因此,我们把图中所有相同的咒语机合并成一个结点,并用num域记录该结点是由多少个结点合并而来(如图1)。
这样,问题转化为在一个有向无环图中求带权最长路经,这同样可以用拓扑排序加上动态规划的方法解决。具体的方法是:首先将图的顶点重新编号使得1,2,…,n是图的拓扑序,然后利用状态转移方程求解即可。
以上两个部分中,第一部分的时间复杂度为O(n2s2),第二部分的时间复杂度为O(s2),所以算法的总时间复杂度为O(n2s2)。
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = ;
int s;
struct node
{
int n,m;
bool ot[N];
int ch[N][];
void read()
{
scanf("%d%d",&n,&m);
for(int a,i=;i<=m;i++)
{
scanf("%d",&a);
ot[a]=;
}
for(int i=;i<n;i++)
{
scanf("%d%d",&ch[i][],&ch[i][]);
}
}
}p[N];
int hed[N],cnt;
struct EG
{
int fr,to,nxt;
}e[N*N],e0[N*N];
void ae(int f,int t)
{
e[++cnt].fr = f;
e[cnt].to = t;
e[cnt].nxt = hed[f];
hed[f] = cnt;
}
struct Pair
{
int x,y;
Pair(){}
Pair(int x,int y):x(x),y(y){}
};
bool vs[N][N];
bool check(node &a,node &b)
{
memset(vs,,sizeof(vs));
queue<Pair>q;
q.push(Pair(a.ch[][],b.ch[][]));
q.push(Pair(a.ch[][],b.ch[][]));
while(!q.empty())
{
Pair u = q.front();
q.pop();
if(!a.ot[u.x]&&b.ot[u.y])return ;
if(vs[u.x][u.y])continue;
vs[u.x][u.y]=;
q.push(Pair(a.ch[u.x][],b.ch[u.y][]));
q.push(Pair(a.ch[u.x][],b.ch[u.y][]));
}
return ;
}
int dep[N],low[N],tot;
bool vis[N];
int bel[N],bc,siz[N],sta[N],tl;
void tarjan(int u)
{
dep[u]=low[u]=++tot;
vis[u]=;
sta[++tl] = u;
for(int j=hed[u];j;j=e[j].nxt)
{
int to = e[j].to;
if(!dep[to])
{
tarjan(to);
low[u] = min(low[u],low[to]);
}else if(vis[to])
{
low[u] = min(low[u],dep[to]);
}
}
if(dep[u]==low[u])
{
bc++;
int c = -;
while(c!=u)
{
c = sta[tl--];
bel[c]=bc;
siz[bc]++;
vis[c] =;
}
}
}
bool eg[N][N];
int Hed[N],Cnt;
void AE(int f,int t)
{
e0[++Cnt].to = t;
e0[Cnt].nxt = Hed[f];
Hed[f] = Cnt;
}
int dp[N];
int dfs(int u)
{
if(dp[u])return dp[u];
if(!Hed[u])return dp[u]=siz[u];
for(int j=Hed[u];j;j=e0[j].nxt)
dp[u]=max(dp[u],dfs(e0[j].to)+siz[u]);
return dp[u];
}
int main()
{
// freopen("pandora.in","r",stdin);
// freopen("pandora.out","w",stdout);
scanf("%d",&s);
for(int i=;i<=s;i++)p[i].read();
for(int i=;i<=s;i++)
for(int j=;j<=s;j++)
if(i!=j&&check(p[i],p[j]))
ae(i,j);
for(int i=;i<=s;i++)
if(!dep[i])tarjan(i);
for(int j=;j<=cnt;j++)
{
int f = bel[e[j].fr],t = bel[e[j].to];
if(f!=t&&!eg[f][t])eg[f][t]=,AE(f,t);
}
int ans = ;
for(int i=;i<=bc;i++)ans=max(ans,dfs(i));
printf("%d\n",ans);
return ;
}
HNOI2006 潘多拉的盒子的更多相关文章
- 图论(Tarjan缩点):BZOJ 1194: [HNOI2006]潘多拉的盒子
1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 344 Solved: 181[Submit][Stat ...
- BZOJ 1194: [HNOI2006]潘多拉的盒子( BFS + tarjan + dp )
O(S²)枚举2个诅咒机, 然后O(n²)BFS去判断. 构成一个有向图, tarjan缩点, 然后就是求DAG的最长路.. ------------------------------------- ...
- 1194: [HNOI2006]潘多拉的盒子
1194: [HNOI2006]潘多拉的盒子 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 464 Solved: 221[Submit][Stat ...
- BZOJ1194: [HNOI2006]潘多拉的盒子(tarjan)
Description 传说中,有个神奇的潘多拉宝盒.如果谁能打开,便可以拥有幸福.财富.爱情.可是直到真的打开,才发现与之 相随的还有灾难.不幸.其实,在潘多拉制造这个宝盒的时候,设置了一些咒语来封 ...
- 1194: [HNOI2006]潘多拉的盒子 - BZOJ
Description Input 第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50).文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语机S-1的顺序描述.每一块的 ...
- BZOJ 1194: [HNOI2006]潘多拉的盒子 [DP DFA]
传送门 题意: s个DFA,选出尽量多的自动机a0, a1, a2, . . . , at,使得a1包含a0.a2包 含a1,以此类推.s ≤ 50. DFA的字符集为{0,1},有的节点是输出源,节 ...
- 【强连通分量】Bzoj1194 HNOI2006 潘多拉的盒子
Description Sulotion 首先要对每对咒语机建图,判断机器a是否能生成所有机器b生成的 如果跑一个相同的串,最后结束的点b可输出a不可输出,判断就为否 大概就用这种思路,f[x][y] ...
- 【bzoj1194】 HNOI2006—潘多拉的盒子
http://www.lydsy.com/JudgeOnline/problem.php?id=1194 (题目链接) 题意 给出S个自动机,如果一个自动机u的所有状态是另一个自动机v的状态的子集,那 ...
- BZOJ 1194 [HNOI2006]潘多拉的盒子 (图论+拓扑排序+tarjan)
题面:洛谷传送门 BZOJ传送门 标签里三个算法全都是提高组的,然而..这是一道神题 我们把这道题分为两个部分解决 1.找出所有咒语机两两之间的包含关系 2.求出咒语机的最长上升序列 我们假设咒语机$ ...
随机推荐
- POJ2533/hdoj1950【DP】
O(nlog(n))的方法: 定义d[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元素. d中元素也是单调递增的. #include <iostrea ...
- bzoj 4557: [JLoi2016]侦察守卫【树形dp】
设f[u][i]为u点向下覆盖至少i层并且处理完u的子树的最小代价,f[u][i]为u点向上覆盖至少i层并且处理完u的子树的最小代价 转移的话显然f[u][i]+=f[v][i-1],但是f[u][0 ...
- 洛谷P3537 [POI2012]SZA-Cloakroom(背包)
传送门 蠢了……还以为背包只能用来维护方案数呢……没想到背包这么神奇…… 我们用$dp[i]$表示当$c$的和为$i$时,所有的方案中使得最小的$b$最大时最小的$b$是多少 然后把所有的点按照$a$ ...
- 左耳朵耗子:我为什么要在极客时间 App 开设独家专栏?
参考链接:https://www.infoq.cn/article/2018/01/why-geektime 不少朋友都知道我在极客时间App 上开了一个收费专栏<左耳听风>,这个专栏会开 ...
- 1-18String类简介
字符串(String)的不可变性 String类在java.lang包下面,是Object类的直接子类,通过API或者源码可以看到,String类是final修饰的,这说明String类不能被继承. ...
- Appium + Python自动化 - 元素定位uiautomatorviewer
元素定位主要介绍如何使用uiautiomatorviewer,通过定位到页面上的元素,然后进行相应的点击等操作.uiautiomatorviewer是android-sdk自带的一个元素定位工具,非常 ...
- 479 Largest Palindrome Product 最大回文数乘积
你需要找到由两个 n 位数的乘积组成的最大回文数.由于结果会很大,你只需返回最大回文数 mod 1337得到的结果.示例:输入: 2输出: 987解释: 99 x 91 = 9009, 9009 % ...
- [未读]JavaScript高效图形编程
去年买来就一直搁置,因为是js游戏相关,暂时还用不到.
- jQuery将json字符串显示在页面上
js代码: function syntaxHighlight(json) { if (typeof json != 'string') { json = JSON.stringify(json, un ...
- Linux系统结构与终端控制台
Linux系统结构与终端控制台 作者:Vashon 时间:20150418 以下主要是对Linux系统终端控制台切换及基本操作的范例,其他的理论就不多说了,直接进入实践部分. Starting.... ...