题意:
有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。求最小死人数和最大死人数。n≤1000000。
题解:
最小死人数:先找到所有入度为1的点放入队列,让它们活,接下来它们杀人,之后又出现入度为1的点,将其放入队列,重复上述操作。直到最后剩一些环,每个环的死人数是(sz[i]+1)/2。
最大死人数:求出联通块,对于一个联通块,如果它只有一个点,那么杀死此人;如果它没有入度为0的点,死人数为sz[i]-1;否则死人数为sz[i]-入度为0的点数。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <queue> 5 #define inc(i,j,k) for(int i=j;i<=k;i++) 6 #define maxn 1000010 7 using namespace std; 8 9 inline int read(){ 10 char ch=getchar(); int f=1,x=0; 11 while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} 12 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); 13 return f*x; 14 } 15 int n,tar[maxn]; 16 struct solve1{ 17 queue<int>q; int cnt[maxn],ans,sz[maxn]; bool die[maxn],ok[maxn]; 18 void dfs(int x){ 19 sz[x]=1; if(!sz[tar[x]])dfs(tar[x]),sz[x]+=sz[tar[x]]; 20 } 21 void solve(){ 22 inc(i,1,n)cnt[tar[i]]++; inc(i,1,n)if(!cnt[i])q.push(i); 23 while(!q.empty()){ 24 int x=q.front(); q.pop(); ok[x]=1; 25 if(!die[tar[x]]){ 26 die[tar[x]]=1; ans++; ok[tar[x]]=1; 27 if(!die[tar[tar[x]]]&&cnt[tar[tar[x]]]){ 28 cnt[tar[tar[x]]]--; if(!cnt[tar[tar[x]]])q.push(tar[tar[x]]); 29 } 30 } 31 } 32 inc(i,1,n)if(!ok[i]&&!sz[i])dfs(i),ans+=(sz[i]+1)/2; 33 printf("%d ",ans); 34 } 35 }solve1; 36 struct solve2{ 37 int cnt[maxn],ans,sz[maxn],g[maxn],ess,tot; struct e{int t,n;}es[maxn*2]; 38 void pe(int f,int t){es[++ess]=(e){t,g[f]}; g[f]=ess;} 39 void dfs(int x){ 40 sz[x]=1; if(!cnt[x])tot++; 41 for(int i=g[x];i;i=es[i].n)if(!sz[es[i].t])dfs(es[i].t),sz[x]+=sz[es[i].t]; 42 } 43 void solve(){ 44 inc(i,1,n)cnt[tar[i]]++; inc(i,1,n){pe(i,tar[i]); pe(tar[i],i);} 45 inc(i,1,n)if(!sz[i]){ 46 tot=0; dfs(i); if(sz[i]==1)ans+=1;else if(!tot)ans+=sz[i]-1;else ans+=sz[i]-tot; 47 } 48 printf("%d",ans); 49 } 50 }solve2; 51 int main(){ 52 n=read(); inc(i,1,n)tar[i]=read(); solve1.solve(); solve2.solve(); return 0; 53 }
20161101