bzoj1124:[POI2008]枪战Maf

传送门

可以知道一共最多只有3种情况:基环树,环,自环
先考虑最少杀死:
首先发现入度为0的点一定能活下来,那么入度为0的点指向的人一定会死,他指向的人指向的人如果入度为0就一定能活下来(其实是不一定的,但是要保证死的人最少)
这样就可以拓扑排序写一下了,剩下的就是环的情况了,能活下来的显然是(lfloor{size/2} floor)
答案就是n-(拓扑排序找出的点)-环的情况活下来的人
再考虑最多杀死:
显然,入度为0的点一定能活,一个单独的环最少只能活下来1个人,而奇环树上的环最少能活下来0个人
答案就是n-(入度为0的点)-环的情况活下来的人
代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
	char ch; bool ok;
	for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
	for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=1e6+10;
int n,a[maxn],iin[maxn],in[maxn],q[maxn],tot,now;bool vis[maxn],used[maxn];
int main()
{
	read(n);
	for(rg int i=1;i<=n;i++)read(a[i]),in[a[i]]++,iin[a[i]]++;
	for(rg int i=1;i<=n;i++)if(!in[i])q[++tot]=i,used[i]=1,now++;
	for(int i=1;i<=tot;i++)
	{
		if(vis[a[q[i]]]||used[a[a[q[i]]]])continue;
		if(!(--in[a[a[q[i]]]]))used[a[a[q[i]]]]=1,q[++tot]=a[a[q[i]]];
		vis[a[q[i]]]=1;
	}
	for(rg int i=1;i<=n;i++)
		if(in[i]>0&&!used[i]&&!vis[i])
		{
			bool flag=0;int sum=0,x=i;
			while(!used[a[x]])flag|=(iin[x]>1),sum++,used[x]=1,x=a[x];
			if(a[x]!=x)sum++,flag|=(iin[x]>1),used[x]=1;
			tot+=sum/2;if(!flag&&a[x]!=x)now++;
		}
	printf("%d %d
",n-tot,n-now);
}