版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
kuangbin带你飞:点击进入新世界
总结:
本人算是初学者中的初学者,欢迎交流~
并查集的接触过的不多,大概只有普通并查集,带权并查集,种族并查集,传说中的可持续化并查集只是听说过还没有接触,不过种族并查集可以用带权并查集来做,带权的话又常常跟dp联系在一起,普通并查集可以作为其他算法的一项工具。
kuangbin的题型:
A.普通并查集 参考1.2.3.6.9
B.带权并查集 参考4.5.7.8
C.并查集+DP
kuangbin之外:
[并查集模板]:待更
文章目录
1.Wireless Network
原题链接:传送门
思路:
- 两点之间的距离预处理map[i][j]表示是否能够通信。
- 指令是o , 要先维护后才能通信。
- 维护一个点之后判断有没有可以其他可以进行通信的点,如果有就加入该点的集合。
代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e3+5;
int a[manx],x[manx],y[manx],vis[manx];
bool map[manx][manx];
int n,d,u,v,l,r;
int find(int f)
{
if(a[f]==f) return f;
return a[f]=find(a[f]);
}
int main()
{
cin>>n>>d;
for(int i=1;i<=n;i++){
scanf("%d%d",&x[i],&y[i]);
a[i]=i;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])<=d*d)
map[i][j]=map[j][i]=true;
char c;
while(cin>>c)
{
if(c=='O'){
scanf("%d",&l);
vis[l]=1;
for(int r=1;r<=n;r++)
{
if(r!=l&&map[l][r]&&vis[r])
{
u=find(l),v=find(r);
if(u==v) continue;
a[u]=v;
}
}
}
else if(c=='S'){
scanf("%d%d",&l,&r);
l=find(l),r=find(r);
if(l==r) printf("SUCCESS\n");
else printf("FAIL\n");
}
}
return 0;
}
2.The Suspects
原题链接:传送门
思路:
- 模板题,一步一步加入集合,最后判断有多少人跟0在同一个集合就行了。
代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=3e4+5;
int a[manx];
int find(int x)
{
if(a[x]==x) return x;
return a[x]=find(a[x]);
}
int main()
{
int n,m,u,v;
while(cin>>n>>m)
{
if(n==0&&m==0) break;
for(int i=0;i<n;i++) a[i]=i;
while(m--)
{
int q;
cin>>q>>u;
for(int i=1;i<q;i++){
cin>>v;
a[find(u)]=find(v);
u=v;
}
}
int ans=0;
for(int i=0;i<n;i++)
if(find(a[i])==find(0))
ans++;
cout<<ans<<endl;
}
return 0;
}
3.How Many Tables
原题链接:传送门
思路:
- 求集合的个数,在连接的时候进行判断即可。
代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
const int manx=1e3+5;
int a[manx];
int n,m;
int find(int x)
{
if(a[x]==x) return x;
else return a[x]=find(a[x]);
}
void show()
{
for(int i=1;i<=n;i++)
a[i]=i;
}
int main()
{
int t;
cin>>t;
while(t--)
{
scanf("%d %d",&n,&m);
int ans=0;
show();
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d %d",&u,&v);
u=find(u),v=find(v);
if(u!=v) ans++;
a[u]=v;
}
printf("%d\n",n-ans);
}
return 0;
}
4.How Many Answers Are Wrong
原题链接:传送门
思路:
- 给一些区间以及其区间和,判断假话语句数量。
- 带权并查集的模板题,根据输入连点,如果已经连好线,判断权值是否相等。
- 关于带权并查集可参考文章开头的模板,欢迎交流。
代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=3e5+5;
int a[manx],d[manx];
int find(int x)
{
if(a[x]==x) return x;
int fa=find(a[x]);
d[x]=d[x]+d[a[x]];
return a[x]=fa;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
int ans=0;
for(int i=0;i<=n;i++) a[i]=i,d[i]=0;
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
u--;
int x=find(u),y=find(v);
if(x==y&&d[v]-d[u]!=w) ans++;
else {
if(x<y) a[y]=x,d[y]=d[u]+w-d[v];
else a[x]=y,d[x]=d[v]-w-d[u];
}
}
cout<<ans<<endl;
}
return 0;
}
5.食物链
原题链接:传送门
思路:
- 同上题,带权并查集入门题目,也可以说是种族并查集,那么只要对权值进行取模即可。
- 注意更新权值的时候需要+3再取模,防止出现负数。
代码如下:
#include<iostream>
#include<cstdio>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=3e5+5;
int a[manx],d[manx];
int find(int x)
{
if(a[x]==x) return x;
int fa=find(a[x]);
d[x]=(d[x]+d[a[x]])%3;
return a[x]=fa;
}
int main()
{
int n,m,ans=0;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) a[i]=i,d[i]=0;
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&w,&u,&v);
w--;
if(u>n||v>n||(w==2&&u==v)) ans++;
else {
int x=find(u),y=find(v);
if(x==y&&(d[v]-d[u]+3)%3!=w) ans++;
else {
a[y]=x;
d[y]=(d[u]+w-d[v]+3)%3;
}
}
}
cout<<ans<<endl;
return 0;
}
6.Supermarket
原题链接:传送门
思路:
- 这道题本来是用贪心来做的,这里参考kuangbin大佬的做法,将并查集作为一个工具,记录每个商品至多能在什么时候卖出。
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int manx=1e4+5;
int a[manx];
struct node{
int w,id;
}d[manx];
bool cmp(node a,node b){
return a.w>b.w;
}
int find(int x)
{
if(a[x]==-1) return x;
else return a[x]=find(a[x]);
}
int main()
{
int n;
while(cin>>n){
for(int i=1;i<=n;i++) cin>>d[i].w>>d[i].id;
memset(a,-1,sizeof(a));
sort(d+1,d+1+n,cmp);
int ans=0;
for(int i=1;i<=n;i++){
int p= find(d[i].id);
if(p>0){
ans+=d[i].w;
a[p]=p-1;
}
}
cout<<ans<<endl;
}
return 0;
}
7.Parity game
原题链接:传送门
思路:
- 跟上面那道区间和相关的带权并查集差不多,不过这里只有奇偶两种情况,那么就是对2取模,即只有01两种情况,因此可以直接异或。
- 主要问题还是多了离散化,数据过大无法创建那么大的节点数组,因此可以把数据映射到一个新的数组里面。
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
const int manx=1e4+5;
struct node{
int l,r;
char s[10];
}q[manx];
int a[manx],d[manx],f[manx];
int find(int x)
{
if(a[x]==x)return x;
else{
int root=find(a[x]);
d[x]^=d[a[x]];
return a[x]=root;
}
}
int main(){
int n,m,k=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%s",&q[i].l,&q[i].r,&q[i].s);
q[i].l--;
f[++k]=q[i].l,f[++k]=q[i].r;
}
sort(f+1,f+1+k);
k=unique(f+1,f+1+k)-f-1;
for(int i=1;i<=k;i++) a[i]=i,d[i]=0;
int i;
for(i=1;i<=m;i++)
{
int l=lower_bound(f+1,f+1+k,q[i].l)-f;
int r=lower_bound(f+1,f+1+k,q[i].r)-f;
int u=find(l),v=find(r);
int w=0;
if(q[i].s[0]=='o') w=1;
if(u==v&&d[l]^w!=d[r]) break;
else if(u<v) a[u]=v,d[u]=d[l]^d[r]^w;
else if(u>v) a[v]=u,d[v]=d[l]^d[r]^w;
}
cout<<i-1<<endl;
return 0;
}
8.A Bug’s Life
原题链接:传送门
思路:
- 跟上道题一样,只有两种情况,一种同性恋,一种异性恋,所以可以用xor来维护权值数组。
代码如下:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
const int manx=2e4+5;
int a[manx],d[manx];
int find(int x)
{
if(a[x]==x) return x;
else {
int fa=find(a[x]);
d[x]^=d[a[x]];
return a[x]=fa;
}
}
int main()
{
int t,T=1;
scanf("%d",&t);
while(t--)
{
int n,m,flag=0;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) a[i]=i,d[i]=0;
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
if(flag) continue;
int fu=find(u),fv=find(v);
if(fu==fv){
if(d[u]==d[v])
flag=1;
}
else a[fv]=fu,d[fv]=d[u]^d[v]^1;
}
cout<<"Scenario #"<<T++<<":"<<endl;
if(flag) puts("Suspicious bugs found!");
else puts("No suspicious bugs found!");
cout<<endl;
}
return 0;
}
9.Is It A Tree?
原题链接:传送门
思路:
- 连通无环,即点数比边数大一。
代码如下:
#include<iostream>
#include<set>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
int n,m;
while(cin>>n>>m)
{
if(n==-1&&m==-1) break;
if(!n&&!m){
puts("Yes");
continue;
}
set<int>q;
q.insert(n),q.insert(m);
int ans=1;
while(cin>>n>>m)
{
if(!n&&!m) break;
ans++;
q.insert(n),q.insert(m);
}
if(ans==q.size()-1) puts("Yes");
else puts("No");
}
return 0;
}