Ufa SATU contest. Petrozavodsk training camp. Summer 2009
【我会说这道题我WA了一夜么……因为在寻找单纯环的时候没处理好预处理 && max 找错了……】
❤题意
找一个队的人。每个人心里都有一个自己认为最好的人【别人 or 自己】。如果你查找一个人,他还不属于任何development team,那么你得为他建立一个新的development team。他认为最好的那个人得和他一组。
问,最少有几组,最多有几组development team。
❤思路
最多的情况就是,成环的人一组【除去爪】。剩下的人一个人一组。
最少的情况是是,单纯成环的人一组。如果一个环上有爪,只有一个爪能与这个环的人同在一组。剩下的成链的人一组。自己和自己单纯成环的就单记了。
存的时候存成指向性,就是第 i 个认为谁最好,就指向谁。没有被指向的,就相当于没有入度。
从没有入度的点找起。【要记录单纯的环的个数】。搜了一遍后,再从未被遍历的点找起。一定只剩下单纯的环。
我的代码写挫了。暂时不想写第二遍了。凑合看吧……
而且写得比较麻烦。貌似有牛人写得超级简单的样子。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define N 100010
int n,vis[N],po[N],cnt[N];
int counthuan,countline;
int countluan;
int len,lenx;
int endd;
int soumin(int x,int y,int ma){
if(vis[po[x]]==1&&po[x]==y){
vis[x]=1;
counthuan=y;
lenx=lenx+len+1;
return 1;
}
else if(vis[po[x]]==1&&po[x]!=y){
vis[x]=1;
counthuan=y;
countluan=1;
endd=x;
return 1;
}
else if(po[x]==0){
vis[x]=1;
countline=y;
return 1;
}
else if(vis[po[x]]==2){
vis[x]=1;
countline=y;
return 1;
}
else if(vis[po[x]]==3){
vis[x]=1;
countline=y;
return 1;
}
else {
vis[x]=1;
len++;
soumin(po[x],y,ma);
}
}
int main(){
while(scanf("%d",&n)!=EOF){
int i,j;
memset(vis,0,sizeof(vis));
memset(po,0,sizeof(po));
memset(cnt,0,sizeof(cnt));
int max=0;
for(i=1;i<=n;i++){
scanf("%d",&j);
if(max<j){max=j;}
if(i!=j){
po[i]=j;
cnt[j]++;
}
else{
po[i]=0;
}
}
if(max<n) max=n;
int ans1=0,ans2=0,cntt2;
int xxx=0;
lenx=0;
for(i=1;i<=n;i++){
counthuan=0;
countline=0;
countluan=0;
len=0;
//从没有入度的点找起。不是单链,就是自己只想自己,要不就是环上的爪。因为单纯成环的点都有入度。
if(!cnt[i]){
if(soumin(i,i,0))
ans1++;
if(counthuan!=0&&countluan==0){
xxx++;
while(vis[po[counthuan]]!=2){
vis[counthuan]=2;
counthuan=po[counthuan];
}
vis[counthuan]=2;
}
else if(counthuan!=0&&countluan!=0){
while(vis[po[endd]]!=2){
vis[endd]=2;
lenx++;
endd=po[endd];
}
vis[endd]=2;
lenx++;
xxx++;
while(vis[po[counthuan]]!=2){
vis[counthuan]=2;
counthuan=po[counthuan];
}
vis[counthuan]=2;
}
if(countline!=0){
while(1){
vis[countline]=3;
countline=po[countline];
if(vis[countline]>1||countline==0){
break;
}
}
}
}
}
//寻找剩下的没又被遍历的点,一定都是构成单纯的环
for(i=1;i<=n;i++){
counthuan=0;
len=0;
if(vis[i]==0){
if(soumin(i,i,0)){
ans1++;
xxx++;
}
if(counthuan!=0){
while(vis[po[counthuan]]!=2){
vis[counthuan]=2;
counthuan=po[counthuan];
}
vis[counthuan]=2;
}
}
}
ans2=xxx+(max-lenx);
printf("%d %d\n",ans1,ans2);
}
return 0;
}