今天主要围绕并查集的一些今典题目展开:
在这里,我们把逻辑真的组合,用并查集即可。一开始,我觉得把a,b,c等价,把第一个赋a,接下来推即可,但这样在判断矛盾时还需要选择合适的点find,于是我们把所有可能合并,这样find时就可以轻松一点,下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
int n,k,fa[200000],cnt;
int find(int x){if(fa[x]==x) return x;else return fa[x]=find(fa[x]);
}
void merge(int x,int y){fa[find(x)]=find(y);
}
int main(){cin>>n>>k;int x,y,z;for(int i=1;i<=3*n;i++) fa[i]=i;for(int i=1;i<=k;i++){scanf("%d%d%d",&x,&y,&z);if(y>n||z>n){cnt++;continue;}if(x==1){if(find(y)==find(z+n)||find(y)==find(z+2*n)) cnt++;else{merge(y,z);merge(y+n,z+n);merge(y+2*n,z+2*n);} }else{if(find(y)==find(z+2*n)||find(y)==find(z)) cnt++;else{merge(y,z+n);merge(y+n,z+2*n);merge(y+2*n,z);} }}cout<<cnt;
}
接下来让我们看看一道有趣的“并拆集”:
首先,假如没有D,只要在根上存那集合上的最大权值,合并时维护一下即可。
那对于D ,我们只要先存D询问,事先把要删的全删,再从后往前合并即可。
接题:
下面为分析:
下面是AC代码:
#include<bits/stdc++.h>
using namespace std;
map<int,int> mp;
int n,m;
int find(int x){if(mp[x]==x) return x;else return mp[x]=find(mp[x]);
}
void merge(int x,int y){mp[find(x)]=find(y);
}
int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y;string s;scanf("%d%d",&x,&y);if(mp.count(y)==0) mp[y]=y;if(mp.count(y+n+1)==0) mp[y+n+1]=y+n+1;if(mp.count(x-1)==0) mp[x-1]=x-1;if(mp.count(x+n)==0) mp[x+n]=x+n;cin>>s;if(x>n||y>n){cout<<i-1;return 0;}if(s=="even"){if(find(x-1)==find(y+n+1)){cout<<i-1;return 0;}else{merge(x-1,y);merge(x+n,y+n+1);}}else{if(find(x-1)==find(y)){cout<<i-1;return 0;}else{merge(x-1,y+n+1);merge(x+n,y);}}}cout<<m;return 0;
}
这里有几个注意的:
1.map离散化 2.注意0也要包括,因此总数为2*n+2