https://www.luogu.org/problemnew/show/P2024
我写了三四天,写了个巨笨的方法
其实2关系能弄出环来,1关系就是合并集合
注意各种情况下的合并,环和环怎么合并,单独集合和环怎么合并,单独集合和单独集合怎么合并
#include<bits/stdc++.h> using namespace std; int n,k; vector <int> S[50000*3+100]; //前50000*2堆用来存环(保证够用),后5000堆存未成环的堆 int rear=100000; //指向最后5000堆 int cnt; //已成多少个环 int M[50000+100]; //Mi表示动物i在哪一堆里 void merge(int x,int y){ //把两个堆合并 int temp=min(x,y); int temp1=max(x,y);//这里直接把下标大的堆的东西整到下标小的堆里去,这种合并任何情况都不会出问题 for(vector<int>::iterator it=S[temp1].begin();it!=S[temp1].end();it++){ S[temp].push_back(*it); } for(vector<int>::iterator it=S[temp1].begin();it!=S[temp1].end();it=S[temp1].begin()){ M[(*it)]=temp; //改一下*it的堆指向 S[temp1].erase(it); //删了大堆 } } int main(){ for(int i=0;i<50000+100;i++) M[i]=-1; cin>>n>>k; int f,x,y,ans=0; for(int i=1;i<=k;i++){ scanf("%d%d%d",&f,&x,&y); if(x>n||y>n){ ans++; continue; } if(f==2&&x==y) //自己不能吃自己 { ans++;continue; } if(f==2 && M[x]!=-1 && M[x]==M[y] ){ //同族不能互吃 ans++;continue; } if(f==2 && M[x]!=-1 && M[y]!=-1 && M[x]<100000 && M[y]<100000 && (M[x]/3==M[y]/3) && ( (M[y]%3==0&&M[x]%3==1) || (M[y]%3==1&&M[x]%3==2) || (M[y]%3==2&&M[x]%3==0))) //不能互吃 { ans++;continue; } if(f==1 && M[x]!=-1 && M[y]!=-1 && M[x]<100000 && M[y]<100000 && ((M[x]/3)==(M[y]/3)) && M[x]!=M[y]){ //异族不能1操作 ans++;continue; } if(f==1){ //合并 if(M[x]==-1&&M[y]==-1){ //都是新来的 S[++rear].push_back(x); //5001后新建堆 插入 更新M M[x]=rear; if(y!=x) //x和y要是一样数组里放个x就完事了不放y了 S[rear].push_back(y); M[y]=rear; continue; } if(M[x]!=-1&&M[y]==-1){ //y是新来的 S[M[x]].push_back(y); //插入 M[y]=M[x]; //更新M continue; } if(M[x]==-1&&M[y]!=-1){ //x是新来的 S[M[y]].push_back(x); //同 M[x]=M[y]; continue; } if(M[x]!=-1&&M[y]!=-1&&M[x]!=M[y]){ //由于之前已经判过不可合并了,他俩现在必可合并 都有堆 if((M[x]>=100001&&M[y]>=100001)||(M[x]<100001&&M[y]>=100001)||(M[y]<100001&&M[x]>=100001)){ // 直接合并 merge(M[x],M[y]); continue; } if(M[x]<100001&&M[y]<100001&&(M[x]/3)!=(M[y]/3)){ //“环”合并 int a1=(M[x]-1); //合并上家 if(M[x]%3==0) a1=M[x]+2; int b1=(M[y]-1); if(M[y]%3==0) b1=M[y]+2; int a2=(M[x]+1); //合并下家 if(M[x]%3==2) a2=M[x]-2; int b2=(M[y]+1); if(M[y]%3==2) b2=M[y]-2; merge(M[x],M[y]); //合并 merge(a1,b1); merge(a2,b2); continue; } } } else{ //k==2 if((M[x]/3==M[y]/3) && ((M[x]%3==0&&M[y]%3==1) || (M[x]%3==1&&M[y]%3==2)||(M[x]%3==2&&M[y]%3==0 ) )) { //已经知道x吃y了,跳过此条信息 continue; } if(M[x]==-1&&M[y]==-1){ //都是新来的 新建环 S[cnt*3+0].push_back(x); //默认放01 0吃1 1吃2 2吃0 M[x]=cnt*3+0; S[cnt*3+1].push_back(y); M[y]=cnt*3+1; cnt++; continue; } if(M[x]!=-1&&M[y]==-1){ //y新来的 if(M[x]<100001){ //x在某个圈里 if(M[x]%3!=2){ //把y放x下家 S[M[x]+1].push_back(y); M[y]=M[x]+1; continue; } else{ S[M[x]-2].push_back(y); M[y]=M[x]-2; continue; } } else{ //x不在圈里 merge(cnt*3+0,M[x]); //新建圈 x堆放圈0位置 S[cnt*3+1].push_back(y); //y放圈1位置 M[y]=cnt*3+1; cnt++; continue; } } if(M[y]!=-1&&M[x]==-1){ //x是新来的 if(M[y]<100001){ //y在圈里 if(M[y]%3!=0){ //把x放y上家 S[M[y]-1].push_back(x); M[x]=M[y]-1; continue; } else{ S[M[y]+2].push_back(x); M[x]=M[y]+2; continue; } } else{ //y不在 S[cnt*3+0].push_back(x); //新建圈 M[x]=cnt*3+0; merge(cnt*3+1,M[y]); cnt++; continue; } } if(M[x]!=-1&&M[y]!=-1){ //xy都是堆 if(M[x]>=100001&&M[y]>=100001){ //都不在圈里 merge(cnt*3+0,M[x]); //新建圈 merge(cnt*3+1,M[y]); cnt++; continue; } if(M[x]<100001&&M[y]>=100001){ //x在圈,y不在 if(M[x]%3==2){ //把y放x下家 merge(M[x]-2,M[y]); continue; } else{ merge(M[x]+1,M[y]); continue; } } if(M[y]<100001&&M[x]>=100001){ //y在圈,x不在 if(M[y]%3==0){ //把x放y上家 merge(M[y]+2,M[x]); continue; } else{ merge(M[y]-1,M[x]); continue; } } if(M[x]<100001&&M[y]<100001&&M[x]/3!=M[y]/3){ //都在圈 //环合并 int a1=(M[x]-1); if(M[x]%3==0) a1=M[x]+2; int b1=(M[y]-1); if(M[y]%3==0) b1=M[y]+2; int a2=(M[x]+1); if(M[x]%3==2) a2=M[x]-2; int b2=(M[y]+1); if(M[y]%3==2) b2=M[y]-2; merge(M[x],b1); merge(M[y],a2); merge(a1,b2); continue; } } } } cout<<ans<<endl; return 0; }