并查集+优先队列+启发式合并 || 罗马游戏 || BZOJ 1455 || Luogu p2713
题面:P2713 罗马游戏
题解:
超级大水题啊,特别水。。
并查集维护每个人在哪个团里,优先队列维护每个团最低分和最低分是哪位,然后每次判断一下哪些人死了,随便写写就行
并查集在Merge时可以用启发式合并,就是把小的团往大的团并,这样显然会更优。当然不写启发式合并应该也能过,就是慢一点。
然后我们也可以写一个按秩合并让它更快233但是没有必要
代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 using namespace std; 5 inline int rd(){ 6 int x=0;char c=getchar(); 7 while(c<'0'||c>'9')c=getchar(); 8 while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} 9 return x; 10 } 11 const int maxn=1000005,maxm=100005; 12 int N,M,fa[maxn],A,B,f1,f2; 13 bool Died[maxn]; 14 char o[2]; 15 struct Peo{ 16 int id,data; 17 bool operator < (const Peo&a)const{ 18 return a.data<data; 19 } 20 }peo; 21 priority_queue<Peo>pri[maxn]; 22 inline int getf(int n){ 23 if(fa[n]==n) return n; 24 fa[n]=getf(fa[n]); 25 return fa[n]; 26 } 27 int main(){ 28 N=rd(); 29 for(int i=1;i<=N;i++){ 30 fa[i]=i; 31 peo.id=i; peo.data=rd(); 32 pri[i].push(peo); 33 } 34 M=rd(); 35 while(M--){ 36 scanf("%s",o); 37 if(o[0]=='M'){ 38 A=rd(); B=rd(); 39 f1=getf(A); f2=getf(B); 40 if(Died[A] || Died[B] || f1==f2) 41 continue; 42 if(pri[f1].size()>pri[f2].size()) swap(f1,f2); 43 while(!pri[f1].empty()){ 44 if(!Died[(pri[f1].top()).id]) 45 pri[f2].push(pri[f1].top()); 46 pri[f1].pop(); 47 } 48 fa[f1]=f2; 49 } 50 else{ 51 A=rd(); 52 if(Died[A]){ 53 printf("0\n"); 54 continue; 55 } 56 f1=getf(A); 57 if(!pri[f1].empty()){ 58 printf("%d\n",(pri[f1].top()).data); 59 Died[(pri[f1].top()).id]=1; 60 pri[f1].pop(); 61 } 62 else printf("0\n"); 63 } 64 } 65 return 0; 66 }
By:AlenaNuna