bzoj1124:[POI2008]枪战Maf
传送门
可以知道一共最多只有3种情况:基环树,环,自环
先考虑最少杀死:
首先发现入度为0的点一定能活下来,那么入度为0的点指向的人一定会死,他指向的人指向的人如果入度为0就一定能活下来(其实是不一定的,但是要保证死的人最少)
这样就可以拓扑排序写一下了,剩下的就是环的情况了,能活下来的显然是\(\lfloor{size/2}\rfloor\)
答案就是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",n-tot,n-now);
}