[POI2008]MAF-Mafia(图论,贪心)
题目描述
Mob feud rages in Equatorial Byteotia. The mob bosses have come to the country's capital, Byteburg, to settle the dispute.
Negotiations were very tense, and at one point the trigger-happy participants drew their guns.
Each participant aims at another with a pistol.
Should they go on a killing spree, the shooting will go in accordance with the following code of honour:
the participants shoot in a certain order, and at any moment at most one of them is shooting, no shooter misses, his target dies instantly, hence he may not shoot afterwards, everyone shoots once, provided he had not been shot before he has a chance to shoot, no participant may change his first target of choice, even if the target is already dead (then the shot causes no further casualties).
An undertaker watches from afar, as he usually does. After all, the mobsters have never failed to stimulate his business.
He sees potential profit in the shooting, but he would like to know tight estimations. Precisely he would like to know the minimum and maximum possible death rate.
The undertaker sees who aims at whom, but does not know the order of shooting.
You are to write a programme that determines the numbers he is so keen to know.
Task Write a programme that:
reads from the standard input what target each mobster has chosen, determines the minimum and maximum number of casualties, writes out the result to the standard output.
给定n个神枪手,每个神枪手瞄准一个人,以一定顺序开枪,问最少和最多死多少人
输入输出格式
输入格式:
The first line of the standard input contains the number of participants ().
They are numbered from to .
The second line contains integers , separated by single spaces, .
denotes the number of participant's target.
Note that it is possible that for some (the nerves, you know).
输出格式:
Your programme should write out two integers separated by a single space in the first and only line of the standard output. These numbers should be, respectively, the minimum and maximum number of casualties resulting from the shooting.
思路:
入度为0的人肯定死不了,跳过
自杀的救不了,跳过
剩下的就是一个仙人掌
对于一个独立的大小为n的环,最后至多剩下n/2个人,最少剩下一个人
对于一个独立的树,根肯定死不了然后就是个树上问题
环套数缩点瞎搞就行
代码:
#include<iostream> #include<cstdio> #include<stack> #define rii register int i using namespace std; int n,to[1000005],e,low[1000005],c[1000005],size[1000005],a1,dfn[1000005]; int a2,xt[1000005],rh[1000005],maxn,minx,rd[1000005]; stack<int> q; bool r[1000005],zh[1000005],ww[1000005],ded[1000005]; int bj(int x,int y) { if(x>=y) { x=y; } } void tarjan(int x) { e++; low[x]=dfn[x]=e; r[x]=1; q.push(x); if(!dfn[to[x]]) { tarjan(to[x]); low[x]=bj(low[x],low[to[x]]); } else { if(r[to[x]]) { low[x]=bj(low[x],dfn[to[x]]); } } if(low[x]==dfn[x]) { a1++; do { a2=q.top(); q.pop(); r[a2]=0; c[a2]=a1; size[a1]++; }while(a2!=x); } } int main() { scanf("%d",&n); for(rii=1;i<=n;i++) { scanf("%d",&to[i]); if(to[i]==i) { minx++; maxn++; ded[i]=1; } rd[to[i]]++; } for(rii=1;i<=n;i++) { if(!dfn[i]) { tarjan(i); } } while(!q.empty()) { q.pop(); } for(rii=1;i<=n;i++) { if(c[i]!=c[to[i]]) { xt[c[i]]=c[to[i]]; rh[c[to[i]]]++; } if(i==to[i]) { zh[c[i]]=1; } } for(rii=1;i<=n;i++) { if(!rd[i]) { q.push(i); } } while(!q.empty()) { a2=q.top(); ww[c[a2]]=1; q.pop(); ww[c[to[a2]]]=1; if(!ded[to[a2]]) { minx++; ded[to[a2]]=1; a2=to[to[a2]]; rd[a2]--; if(rd[a2]==0&&!ded[a2]) { q.push(a2); } } } for(rii=1;i<=a1;i++) { if(size[i]!=1&&!ww[i]) { if(size[i]&1) { a2=(size[i]+1)>>1; } else { a2=size[i]>>1; } minx+=a2; } if(size[i]!=1&&!rh[i]) { maxn+=size[i]-1; } if(size[i]!=1&&rh[i]) { maxn+=size[i]; } if(size[i]==1&&rh[i]&&!zh[i]) { maxn++; } } printf("%d %d",minx,maxn); }