并查集总结
并查集总结
维护连通块的数量的话 可以在修改f[t1]=t2的时候 让sum[t2]+=sum[t1] 即可
在维护每个点到根节点的距离的时候 需要在并查集的过程中维护 即 t=f][x] d[x]+=d[t]
题目链接:https://www.acwing.com/problem/content/1252/
转换题意 判断什么时候出现环, 当find1(a)==find1(b) 已经存在的时候,再连上一条边就一定出现环
然后就是二维坐标转换成一维坐标 即 p=y+(x-1)*n
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=4e4+10; 4 const int mod=998244353; 5 #define ll long long 6 int f[maxn]; 7 int find1(int x) 8 { 9 if(x==f[x]) return x; 10 return f[x]=find1(f[x]); 11 } 12 13 void add(int x,int y) 14 { 15 int t1=find1(x),t2=find1(y); 16 f[t2]=t1; 17 } 18 19 int main() 20 { 21 ios::sync_with_stdio(0); 22 cin.tie(0); 23 int n,m; 24 cin>>n>>m; 25 for(int i=1;i<=n*n;i++) 26 f[i]=i; 27 int ans=0; 28 for(int i=1;i<=m;i++) 29 { 30 int x,y; 31 char d; 32 cin>>x>>y>>d; 33 if(ans) continue; 34 int u=y+(x-1)*n; 35 int v=u; 36 if(d=='R') v++; 37 else v+=n; 38 if(find1(u)==find1(v)) 39 ans=i; 40 add(u,v); 41 } 42 if(!ans) cout<<"draw"<<'\n'; 43 else cout<<ans<<'\n'; 44 45 46 47 48 49 }
题目链接:https://www.acwing.com/problem/content/1254/
弄成一个个连通块 然后 跑01背包问题即可
如何弄成一个个连通块并且将w和v累加起来呢? 只需要在add里面的时候每次 将子节点的值累加到父节点即可
最后每个根节点代表一个连通块
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e4+10; 4 const int mod=998244353; 5 #define ll long long 6 #define pi pair<int,int> 7 #define fi first 8 #define sc second 9 #define pb push_back 10 int f[maxn]; 11 int dp[maxn],w[maxn],v[maxn]; 12 13 int find1(int x) 14 { 15 if(x==f[x]) return x; 16 return f[x]=find1(f[x]); 17 } 18 19 void add(int x,int y) 20 { 21 int t1=find1(x),t2=find1(y); 22 if(t1==t2) return; 23 f[t1]=t2; 24 w[t2]+=w[t1]; 25 v[t2]+=v[t1]; 26 } 27 28 29 int main() 30 { 31 ios::sync_with_stdio(0); 32 cin.tie(0); 33 int n,m,V; 34 cin>>n>>m>>V; 35 for(int i=1;i<=n;i++) f[i]=i; 36 for(int i=1;i<=n;i++) cin>>v[i]>>w[i]; 37 while(m--) 38 { 39 int u,v; 40 cin>>u>>v; 41 add(u,v); 42 } 43 for(int i=1;i<=n;i++) 44 if(i==find1(i)) 45 for(int j=V;j>=v[i];j--) 46 dp[j]=max(dp[j],dp[j-v[i]]+w[i]); 47 cout<<dp[V]<<'\n'; 48 49 50 51 }
题目链接:https://www.acwing.com/problem/content/240/ ###K
用d[]数组来维护 节点到根节点的距离 可以知道每次 两段互相接上的时候 d[t1]=sum[t2] 即偏移量增加这么多
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=3e4+10; 4 const int mod=1e9+7; 5 #define ll long long 6 #define pi pair<int,int> 7 #define fi first 8 #define sc second 9 #define pb push_back 10 int f[maxn]; 11 int d[maxn]; 12 int sum[maxn]; 13 14 int find1(int x) 15 { 16 if(x==f[x]) return f[x]; 17 else 18 { 19 int t=f[x]; 20 f[x]=find1(f[x]); 21 d[x]+=d[t]; 22 } 23 return f[x]; 24 } 25 26 27 int main() 28 { 29 ios::sync_with_stdio(0); 30 cin.tie(0); 31 int t; 32 cin>>t; 33 for(int i=1;i<maxn;i++) 34 f[i]=i,sum[i]=1; 35 while(t--) 36 { 37 char a; 38 int b,c; 39 cin>>a>>b>>c; 40 if(a=='M') 41 { 42 int t1=find1(b),t2=find1(c); 43 f[t1]=t2; 44 if(t1!=t2) 45 d[t1]=sum[t2]; 46 sum[t2]+=sum[t1]; 47 } 48 else 49 { 50 int t1=find1(b),t2=find1(c); 51 if(t1!=t2) 52 cout<<-1<<'\n'; 53 else 54 cout<<abs(d[b]-d[c])-1<<'\n'; 55 } 56 } 57 58 59 60 61 62 }
传送门:https://www.cnblogs.com/winfor/p/14115769.html 四道题 都是带权并查集 即d[x]+=d[t] 这种
题目链接:https://www.acwing.com/problem/content/241/ ###K
思路:首先用前缀和的思想把题目转换成 给定l和r 等价于 l-1 和r 是否是同一类,奇数代表不同一类,偶数代表同一类
然后每次判断的时候,先判断着两个数是否已经有关系(即是否在一个连通块中) 如果有关系的时候根据当前给定判断是否矛盾
否则的话 就带权并查集并在一起, 关于 新建的边权是多少参考下图

1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=2e4+10; 4 const int mod=1e9+7; 5 #define ll long long 6 #define pi pair<int,int> 7 #define fi first 8 #define sc second 9 #define pb push_back 10 unordered_map<int,int>mp; 11 int f[maxn],d[maxn]; 12 13 14 int tot; 15 int find1(int x) 16 { 17 if(x!=f[x]) 18 { 19 int t=f[x]; 20 f[x]=find1(f[x]); 21 d[x]^=d[t]; 22 } 23 return f[x]; 24 } 25 int get(int x) 26 { 27 if(!mp[x]) mp[x]=++tot; 28 return mp[x]; 29 } 30 31 32 33 int main() 34 { 35 ios::sync_with_stdio(0); 36 cin.tie(0); 37 int n,m; 38 cin>>n>>m; 39 int ans=m; 40 for(int i=1;i<maxn;i++) f[i]=i; 41 for(int i=0;i<m;i++) 42 { 43 int x,y; 44 string s; 45 cin>>x>>y>>s; 46 if(ans!=m) continue; 47 x=get(x-1),y=get(y);; 48 int t1=find1(x),t2=find1(y); 49 if(s[0]=='e') 50 { 51 if(t1==t2) 52 { 53 if(d[x]^d[y]) 54 ans=i; 55 } 56 else 57 { 58 f[t1]=t2; 59 d[t1]=d[x]^d[y]; 60 } 61 } 62 else 63 { 64 if(t1==t2) 65 { 66 if(!(d[x]^d[y])) 67 ans=i; 68 } 69 else 70 { 71 f[t1]=t2; 72 d[t1]=d[x]^d[y]^1; 73 } 74 } 75 } 76 cout<<ans<<'\n'; 77 78 79 80 81 82 83 }
这次用了无序的离散化, 并且unorder_map 比map更快 可以达到o(1) 不过cf上会被卡成o(n)
用map的好处是在线离散化好写代码, 如果用二分的那个离散化是离线的,需要全部存下来再离散化再处理
而且这里的异或都要写异或,如果用加法的话取得取模防止溢出
如果是根节点的话 d[p]=0 所以每次就算不断查询 也不会修改到当前d[x]的值 d[x]^0=d[x]
// 也可以用扩展域并查集来写
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=4e4+10; 4 const int mod=1e9+7; 5 #define ll long long 6 #define pi pair<int,int> 7 #define fi first 8 #define sc second 9 #define pb push_back 10 unordered_map<int,int>mp; 11 int f[maxn],d[maxn]; 12 13 14 int tot; 15 int find1(int x) 16 { 17 if(x==f[x]) return x; 18 return f[x]=find1(f[x]); 19 } 20 void add(int x,int y) 21 { 22 int t1=find1(x),t2=find1(y); 23 f[t1]=t2; 24 } 25 int get(int x) 26 { 27 if(!mp[x]) mp[x]=++tot; 28 return mp[x]; 29 } 30 31 32 33 int main() 34 { 35 ios::sync_with_stdio(0); 36 cin.tie(0); 37 int n,m; 38 cin>>n>>m; 39 int ans=m; 40 for(int i=1;i<maxn;i++) f[i]=i; 41 for(int i=0;i<m;i++) 42 { 43 int x,y; 44 string s; 45 cin>>x>>y>>s; 46 if(ans!=m) continue; 47 x=get(x-1),y=get(y);; 48 if(s[0]=='e') 49 { 50 if(find1(x+maxn/2)==find1(y)) 51 { 52 ans=i; 53 } 54 add(x,y); 55 add(x+maxn/2,y+maxn/2); 56 } 57 else 58 { 59 if(find1(x)==find1(y)) 60 { 61 ans=i; 62 } 63 add(x+maxn/2,y); 64 add(x,y+maxn/2); 65 } 66 } 67 cout<<ans<<'\n'; 68 69 70 71 72 73 74 }
主要思路是 x代表x是奇数这个条件,y代表y是奇数这个条件 x+n 和y+n 代表x是偶数,y是偶数的条件
那么每次读取条件的时候,我们只需要把 这些x x+n y y+n 按照实际意义合并在同一个集合中,到时候再判断是否矛盾即可
注意 加边的时候我们加的是对称边,所以判断条件的时候,只需要判断一次即可 比如 find(x)==find(y) 因为如果这个条件成立
那么之前添加条件的时候肯定也添加了 find(x+n)==find(y+n)
// 对于处理相对关系的 用可扩展域并查集更好理解, 只有在处理权值的时候才使用带权并查集
// 注意 用可扩展域并查集的话 判断矛盾的时候只需要判断一对关系,因为添加关系的时候添加了对称的关系
题目链接:https://www.acwing.com/problem/content/242/ ###K
这种矛盾的问题 用扩展域并查集来写会更加清楚好写
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=5e4+10; 4 const int mod=1e9+7; 5 #define ll long long 6 #define pi pair<int,int> 7 #define fi first 8 #define sc second 9 #define pb push_back 10 int f[maxn*3]; 11 12 int find1(int x) 13 { 14 if(x==f[x]) return x; 15 return f[x]=find1(f[x]); 16 } 17 18 void add(int x,int y) 19 { 20 int t1=find1(x),t2=find1(y); 21 f[t1]=t2; 22 } 23 24 25 int main() 26 { 27 ios::sync_with_stdio(0); 28 cin.tie(0); 29 int n,k; 30 cin>>n>>k; 31 for(int i=1;i<=n*3;i++) f[i]=i; 32 int ans=0; 33 while(k--) 34 { 35 int d,x,y; 36 cin>>d>>x>>y; 37 if(x>n||y>n) 38 { 39 ans++; 40 continue; 41 } 42 if(d==1) 43 { 44 if(find1(x+n)==find1(y)||find1(x+n+n)==find1(y)) 45 ans++; 46 else 47 { 48 add(x,y); 49 add(x+n,y+n); 50 add(x+n+n,y+n+n); 51 } 52 } 53 else 54 { 55 if(x==y||find1(x)==find1(y)||find1(x+n+n)==find1(y)) 56 ans++; 57 else 58 { 59 add(x+n,y); 60 add(x,y+n+n); 61 add(x+n+n,y+n); 62 } 63 } 64 } 65 cout<<ans<<'\n'; 66 67 68 69 70 71 }
x为同类域 x+n为捕食域 x+n+n为天敌域
那么当第一类问题的时候 如果x捕食y 或者x的天敌是y 那么就矛盾了
否则的话 将x的三类域都添加上边
当第二类问题的时候 如果x和y同类那么矛盾,如果x的天敌是y 那么也矛盾
否则的话 将x的三类域都添加上边
每次只需要判完x的部分就行了 因为y跟x对称

浙公网安备 33010602011771号