zoukankan      html  css  js  c++  java
  • 【BZOJ1124】Mafia(POI2008)-环套树DP

    测试地址:Mafia
    做法: 本题需要用到环套树DP。
    按照题目构图,很显然是我们很熟悉的环套树森林。接下来我们进行分析,最后活下来一些什么人是合法的呢?观察发现,一个人的目标如果是自己那就必死,而没有被作为目标的人一定存活,还有一个特别重要的性质:一个人ii和他的目标aia_i到最后不可能都存活。在满足这些条件的情况下,我们一定可以构造出一个顺序使得最后存活特定的人。于是问题就非常好分析了。
    首先来看怎么样死亡最多,也就是存活最少。由于上面我们知道,没有被作为目标的人一定存活,那么其他的人是一定都会死吗?不一定,因为我们再次发现,一个连通块内至少会有一个人存活。因此我们对所有没被作为目标的人打个标记,在进行连通块的搜索的时候,如果当前连通块内不存在这样的人(事实上,这种情况只有可能是一个环),那么就会多出一个人存活。当然,如果这个环内只有一个人,那么因为这个人目标是自己,所以他必死,特判一下即可。这样我们就能O(n)O(n)计算出这个答案了。
    然后我们来看怎么样死亡最少,也就是存活最多。注意到上面“一个人和他的目标不能都存活”这个性质,在图中就表现为,一条边的两个端点不可能都存活,于是我们要找的就是在这种状态下,最多能选出多少个人存活,这显然就是一个环套树上的最大独立集问题,只不过还要规定所有叶子节点都必须被选,用边界条件稍微修改的环套树DP就能O(n)O(n)解决。当然,还要特判环套树中的环中只有一个人的情况,这样的话这个人是必死的。把每个连通块的答案加起来,就是最多的存活人数了,最少的死亡人数也随之得出了。
    于是经过上面的讨论,我们解决了这个问题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,a[1000010],in[1000010],q[1000010],h,t,ans1,ans2;
    int f[1000010][2]={0},looplen,loop[1000010];
    int loopf[2][2];
    bool totflag,flag[1000010]={0},vis[1000010]={0};
    
    void find_loop(int i)
    {
    	looplen=1;
    	totflag=flag[i];
    	loop[1]=i;
    	vis[i]=1;
    	f[i][1]++;
    	while(a[loop[looplen]]!=loop[1])
    	{
    		++looplen;
    		loop[looplen]=a[loop[looplen-1]];
    		totflag|=flag[loop[looplen]];
    		vis[loop[looplen]]=1;
    		f[loop[looplen]][1]++;
    	}
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		in[a[i]]++;
    	}
    	
    	h=1,t=0;
    	ans2=n;
    	for(int i=1;i<=n;i++)
    		if (!in[i])
    		{
    			q[++t]=i;
    			ans2--;
    		}
    	while(h<=t)
    	{
    		int v=q[h++];
    		in[a[v]]--;
    		f[v][1]++;
    		f[a[v]][0]+=max(f[v][0],f[v][1]);
    		f[a[v]][1]+=f[v][0];
    		if (!in[a[v]]) q[++t]=a[v];
    		flag[a[v]]=1;
    	}
    	
    	ans1=n;
    	for(int i=1;i<=n;i++)
    		if (in[i]&&!vis[i])
    		{
    			find_loop(i);
    			if (!totflag&&looplen>1) ans2--;
    			if (looplen==1) {ans1-=f[i][0];continue;}
    			if (looplen==2)
    			{
    				int x=i,y=a[i],mx=0;
    				mx=max(mx,f[x][0]+f[y][0]);
    				mx=max(mx,f[x][0]+f[y][1]);
    				mx=max(mx,f[x][1]+f[y][0]);
    				ans1-=mx;
    				continue;
    			}
    			if (looplen==3)
    			{
    				int x=i,y=a[i],z=a[a[i]],mx=0;
    				mx=max(mx,f[x][0]+f[y][0]+f[z][0]);
    				mx=max(mx,f[x][0]+f[y][0]+f[z][1]);
    				mx=max(mx,f[x][0]+f[y][1]+f[z][0]);
    				mx=max(mx,f[x][1]+f[y][0]+f[z][0]);
    				ans1-=mx;
    				continue;
    			}
    			int mx=0,now=1,past=0;
    			loopf[past][0]=loopf[past][1]=0;
    			for(int j=2;j<=looplen;j++)
    			{
    				loopf[now][0]=max(loopf[past][0],loopf[past][1])+f[loop[j]][0];
    				loopf[now][1]=loopf[past][0]+f[loop[j]][1];
    				swap(now,past);
    			}
    			mx=max(mx,max(loopf[past][0],loopf[past][1])+f[loop[1]][0]);
    			loopf[past][0]=loopf[past][1]=0;
    			for(int j=3;j<=looplen-1;j++)
    			{
    				loopf[now][0]=max(loopf[past][0],loopf[past][1])+f[loop[j]][0];
    				loopf[now][1]=loopf[past][0]+f[loop[j]][1];
    				swap(now,past);
    			}
    			mx=max(mx,max(loopf[past][0],loopf[past][1])+f[loop[1]][1]+f[loop[2]][0]+f[loop[looplen]][0]);
    			ans1-=mx;
    		}
    	
    	printf("%d %d",ans1,ans2);
    	
    	return 0;
    }
    
  • 相关阅读:
    Python--前端之HTML
    Python--MySql(主键的创建方式、存储引擎、存储过程、索引、pymsql)
    python--MySql(外键约束、多表查询(*****))
    python--MySql 表记录的操作
    python--MySql
    Python--线程队列(queue)、multiprocessing模块(进程对列Queue、管道(pipe)、进程池)、协程
    Python--同步锁(互斥锁)、死锁(状态)、递归锁、信号量、Event对象
    Python--多线程、多进程常用概念
    Python--基础之socket编程
    ubuntu 安装 flashplayer
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793246.html
Copyright © 2011-2022 走看看