bzoj1124[POI2008]枪战Maf*

bzoj1124[POI2008]枪战Maf

题意:

有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

转载于:https://www.cnblogs.com/YuanZiming/p/6038718.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值