题目大意:给定
n
个神枪手,每个神枪手瞄准一个人,以一定顺序开枪,问最少和最多死多少人
首先考虑最多
对于每个联通块:
如果这个连通块只有一个人,那么这个人自杀,死亡人数为
1
如果这个连通块是一个环,那么可以活下来一个人,死亡人数为
size?1
否则除了叶节点之外其他人都可以死,死亡人数为
size?cnt叶节点
接下来考虑最少
首先叶节点一定不能死
首先把叶节点加入队列,然后每取出一个点时,击杀他瞄准的人,然后如果他瞄准的人瞄准的人此时成为了一个叶节点,那么把这个人加入队列
最后会剩下一些环,一个大小为
size
的环死亡人数为
?size2?
#include
#include
#include
#include
#define M 1001001 using namespace std; int n,ans1,ans2; int a[M],degree[M]; namespace Solver1{ int degree[M]; bool v[M],dead[M]; void Solve() { static int q[M],r,h; int i; memcpy(degree,::degree,sizeof degree); for(i=1;i<=n;i++) if(!degree[i]) q[++r]=i; while(r!=h) { int x=q[++h]; v[x]=true; if(!dead[a[x]]) { v[a[x]]=dead[a[x]]=true;ans1++; if( !--degree[a[a[x]]] ) q[++r]=a[a[x]]; } } for(i=1;i<=n;i++) if(!v[i]) { int cnt=0,x=i; while(1) { if(v[x]) break ; v[x]=true;cnt++; x=a[x]; } ans1+=cnt+1>>1; } } } namespace Solver2{ struct abcd{ int to,next; }table[M<<1]; int head[M],tot; bool v[M]; int stack[M],top; void Add(int x,int y) { table[++tot].to=y; table[tot].next=head[x]; head[x]=tot; } void BFS(int x) { static int q[M],r,h; int i; q[++r]=x;v[x]=true; while(r!=h) { x=q[++h]; stack[++top]=x; for(i=head[x];i;i=table[i].next) if(!v[table[i].to]) v[table[i].to]=true,q[++r]=table[i].to; } } void Solve() { int i; for(i=1;i<=n;i++) { Add(i,a[i]); Add(a[i],i); } for(i=1;i<=n;i++) if(!v[i]) { top=0; BFS(i); if(top==1) ans2++; else { int cnt=0,size=top; while(top) cnt+=degree[stack[top--]]==0; if(cnt==0) ans2+=size-1; else ans2+=size-cnt; } } } } int main() { int i; cin>>n; for(i=1;i<=n;i++) { scanf("%d",&a[i]); degree[a[i]]++; } Solver1::Solve(); Solver2::Solve(); cout<