刷題記錄 kuangbin帶你飛專題五:並查集

由於在學帶權並查集的過程中不斷的自閉,決定分步寫這個專題的題解

1.POJ 2236 Wireless Network

普通並查集,把點亮的網絡跟每一個距離內的連通

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int n,m,k,d;
int a[300005];
inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}
inline bool juli(double x1,double y1,double x2,double y2){
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<=d;
}
int find(int x){
	if(x==a[x])return x;
	else a[x]=find(a[x]);
}
void hb(int x,int y){
	a[find(x)]=find(y);
	return;
}
struct node{
	double x,y;
	bool ok;
}pos[300005];
int main(){
	cin>>n>>d;
	for(int i=1;i<=n;i++){
		a[i]=i;
	}
	for(int i=1;i<=n;i++){
		pos[i].x=read();
		pos[i].y=read();
		pos[i].ok=false;
	}
	char ch;
	int t,p;
	while(scanf("%c",&ch)!=EOF){
		if(ch=='O'){
			t=read();
			if(pos[t].ok)continue;
			pos[t].ok=true;
			for(int i=1;i<=n;i++){
				if(pos[i].ok==true&&juli(pos[i].x,pos[i].y,pos[t].x,pos[t].y)){
					hb(i,t);
				}
			}
		}
		else{
			t=read(),p=read();
			if(find(t)==find(p))printf("SUCCESS\n");
			else printf("FAIL\n");
		}
	}
	return 0;
}

2.POJ 1611 The Suspects

求多少人跟0在同一集合

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int n,m,k,t,p1,p2,ans;
int a[30005];
int find(int x){
	if(x==a[x])return x;
	return a[x]=find(a[x]);
}
void hb(int x,int y){
	if(x>y)swap(x,y);
	a[find(y)]=find(x);
	return;
}
int main(){
	while(cin>>n>>m){
		for(int i=0;i<n;i++){
			a[i]=i;
		}
		ans=1;
		if(n==0&&m==0)break;
		vector<int>v;
		for(int i=1;i<=m;i++){
			k=read(),p1=read();
			for(int j=2;j<=k;j++){
				p2=read();
				hb(p1,p2);
			}
		}
		for(int i=1;i<n;i++){
			if(find(i)==find(0))ans++;
		}
		cout<<ans<<endl;
	}
}

3.HDU 1213 How Many Tables

求有多少個不同的集合

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int n,m,k,t,p1,p2,ans;
int a[30005];
int find(int x){
	if(x==a[x])return x;
	return a[x]=find(a[x]);
}
void hb(int x,int y){
	if(x>y)swap(x,y);
	a[find(y)]=find(x);
	return;
}
int vis[5005];
int main(){
	cin>>t;
	while(t--){
		cin>>n>>m;
		ans=0;
		for(int i=0;i<=n;i++){
			a[i]=i;
		}
		for(int i=1;i<=m;i++){
			int x,y;
			x=read(),y=read();
			hb(x,y);
		}
		for(int i=1;i<=n;i++){
			if(vis[find(i)]==0){
				ans++;
				vis[find(i)]++;
			}
		}
		memset(vis,0,sizeof(vis));
		cout<<ans<<endl;
	}
	return 0;
}

4.HDU 3038 How Many Answers Are Wrong

帶權並查集的模板,自閉的開始
我目前對它的理解就是,用每個點跟祖先的距離前綴和(如果給的是區間)or值(給的是兩個單點)來記錄信息,如果是同一祖先,判斷兩個點的距離差是否滿足條件,如果不在同一集合,就將a的祖先連到b的祖先上,並賦上權值

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int n,m,k,ans;
int a[200005];
int d[200005];
int find(int x){
	if(x==a[x])return x;
	int t=find(a[x]);
	d[x]+=d[a[x]];
	a[x]=t;
	return t;
}
void hb(int x,int y){
	if(x>y)swap(x,y);
	a[find(y)]=find(x);
	return;
}
int main(){
	while(~scanf("%d %d",&n,&m)){
		ans=0;
		for(int i=0;i<=n;i++){
			a[i]=i;
			d[i]=0;
		}
		for(int i=1;i<=m;i++){
			int x,y,z;
			x=read(),y=read(),z=read();
			x--;
			int fx=find(x);
			int fy=find(y);
			if(fx==fy&&d[y]-d[x]!=z){
				ans++;
				continue;
			}
			else if(fx!=fy){
				a[fx]=fy;
				d[fx]=d[y]-d[x]-z;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

5.POJ 1182 食物鏈

最經典的例題,有兩種寫法,種羣並查集和帶權
帶權的寫法就是判斷 %3相關,如果d[x]-d[y]%3==0說明是同種生物 ==1代表捕食 ==2代表被捕食這樣子

帶權寫法

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int n,m,k,t,ans,f[50005],d[50005];
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}

int find(int x){
	if(f[x]==x)return x;
	int root=f[x];
	f[x]=find(f[x]);
	d[x]=(d[x]+d[root])%3;
	return f[x];
}
void hb(int x,int y,int z){
	int fx=find(x);
	int fy=find(y);
	f[fy]=fx;
	d[fy]=(d[x]-d[y]+3+z-1)%3;
}
int main(){
	cin>>n>>k;
	ans=0;
	for(int i=1;i<=n;i++){
		f[i]=i;
		d[i]=0;
	}
	for(int i=1;i<=k;i++){
		int tt,x,y;
		tt=read(),x=read(),y=read();
		if(x>n||y>n){
			ans++;
			continue;
		}
		else if(tt==2&&x==y){
			ans++;
			continue;
		}
		int fx=find(x);
		int fy=find(y);
		if(fx==fy){
			if(tt==1&&d[x]!=d[y])ans++;
			if(tt==2&&(d[x]+1+3)%3!=d[y])ans++;
		}
		else{
			hb(x,y,tt);
		}
	}
	cout<<ans;
	return 0;
}

種族並查集寫法:開個3*n的數組,每次分別存a,b,c的關係

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int a[150005],n,m,k,t,ans;
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int find(int x){
	if(x==a[x])return x;
	return a[x]=find(a[x]);
}
bool same(int x,int y){
	return find(x)==find(y);
}
void unite(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y)return;
	a[x]=y;
}
int main(){
	cin>>n>>k;
	{
		ans=0;
		for(int i=0;i<n*3;i++){
			a[i]=i;
		}
		for(int i=1;i<=k;i++){
			t=read();
			int x,y;
			x=read(),y=read();
			x--,y--;
			if(x<0||y<0||x>=n||y>=n){
		    	ans++;
		    	continue;
	    	}
			if(t==1){
				if(same(x,y+n)||same(x,y+2*n)){
					ans++;
					continue;
				}
				else{
					unite(x,y);
					unite(x+n,y+n);
					unite(x+2*n,y+2*n);
				}
			}
			else{
				if(same(x,y)||same(x,y+2*n)){
					ans++;
					continue;
				}
				else{
					unite(x,y+n);
					unite(x+n,y+2*n);
					unite(x+2*n,y);
				}
			}
		}
		cout<<ans<<endl;
	}
}

6.POJ 1417 True Liars

帶權+dp 以後補

7.POJ 1456 Supermarket

先按價值排序,然後遍歷,如果在這個商品的到期日之前還有剩餘的日子就可以買它,否則跳過

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int n,m,k,t,ans,f[10005];
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
struct node{
	int s,d;
}a[10005];
bool cmp(node a,node b){
	return a.s>b.s;
}
int find(int x){
	if(f[x]==-1)return x;
	return f[x]=find(f[x]);
}
int main(){
	while(cin>>n){
		ans=0;
		memset(f,-1,sizeof(f));
		memset(a,0,sizeof(a));
		for(int i=1;i<=n;i++){
			a[i].s=read();
			a[i].d=read();
		}
		sort(a+1,a+1+n,cmp);
		for(int i=1;i<=n;i++){
			int t=find(a[i].d);
			if(t>0){
				ans+=a[i].s;
				f[t]=t-1;
			}
		}
		cout<<ans<<endl;
	}
}

8.POJ 1733 Parity game

數據很大所以要離散化
前綴和數組,d[y]^d[x]=0即爲偶數,爲1則是奇數

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int n,m,k,t,ans,f[50005],d[50005];
map<int,int>mp;
int cnt=0;
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int lis(int x){
	if(mp.find(x)==mp.end())mp[x]=cnt++;
	return mp[x];
}
int find(int x){
	if(f[x]==-1)return x;
	int root=find(f[x]);
	d[x]^=d[f[x]];
	f[x]=root;
	return f[x];
}
bool hb(int x,int y,int z){
	int fx=find(x);
	int fy=find(y);
	if(fx==fy){
		if(d[x]^d[y]==z){
			ans++;
			return true;
		}
		else return false;
	}
	else{
		f[fx]=fy;
		d[fx]=d[x]^d[y]^z;
		ans++;
		return true;
	}
}
int main(){
	cin>>n>>k;
	ans=0;
	int fa=0;
	for(int i=0;i<=50005;i++){
		f[i]=-1;
		d[i]=0;
	}
	for(int i=1;i<=k;i++){
		int x,y;
		string a;
		x=read(),y=read();
		if(x>y)swap(x,y);
		x=lis(x-1);
		y=lis(y);
		cin>>a;
		if(fa==1)continue;
		int w;
		if(a[0]=='e')w=0;
		else w=1;
		if(hb(x,y,w)==false)fa=1;
	}
	cout<<ans;
	return 0;
}

9.POJ 1984 Navigation Nightmare

其實在被帶權折磨幾天理解帶權並查集的核心思想之後
這題好像也就是個水題(?)
只不過變成了二維,然後題目的查詢中要求遍歷到第I條信息的時候的曼哈頓距離,可以先用結構體把之前的信息都存起來,在詢問的過程中合併就可以了

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int dx[50005],dy[50005];
int f[50005];
int n,m,k,a,b,l,d,dd;
struct node{
	int a,b,l;
	char d[10];
}e[50005];
int find(int x){
	if(x==f[x])return x;
	int root=find(f[x]);
	dx[x]+=dx[f[x]];
	dy[x]+=dy[f[x]];
	f[x]=root;
	return f[x];
}
void hb(int x,int y,char d,int z){
	int fx=find(x);
	int fy=find(y);
	f[fy]=fx;
	if(d=='W'){
		dx[fy]=dx[x]-dx[y]-z;
		dy[fy]=dy[x]-dy[y];
	}
	if(d=='E'){
		dx[fy]=dx[x]-dx[y]+z;
		dy[fy]=dy[x]-dy[y];
	}
	if(d=='N'){
		dx[fy]=dx[x]-dx[y];
		dy[fy]=dy[x]-dy[y]+z;
	}
	if(d=='S'){
		dx[fy]=dx[x]-dx[y];
		dy[fy]=dy[x]-dy[y]-z;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		f[i]=i;
		dx[i]=0;
		dy[i]=0;
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d%d%s",&e[i].a,&e[i].b,&e[i].l,e[i].d);
	}
	scanf("%d",&k);
	for(int i=1,j=1;i<=k;i++){
		int x,y,dd;
		scanf("%d%d%d",&x,&y,&dd);
		while(j<=dd){
			int xx=e[j].a;
			int yy=e[j].b;
			int fx=find(xx);
	    	int fy=find(yy);
			if(fx!=fy){
		    	hb(e[j].a,e[j].b,e[j].d[0],e[j].l);
			}
			j++;
		}
		int fx=find(x);
		int fy=find(y);
		if(fx!=fy){
			printf("-1\n");
		}
		else{
			int s=abs(dy[y]-dy[x])+abs(dx[y]-dx[x]);
			printf("%d\n",s);
		}
	}
	return 0;
}

10.POJ 2492 A Bug’s Life

其實跟食物鏈是同一個題,只不過那題要%3這題%2,而%2的情況就可以用 異或^ 來代替

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
int f[50005],d[50005],n,m,k;
int find(int x){
	if(f[x]==x)return x;
	int root=find(f[x]);
	d[x]^=d[f[x]];
	return f[x]=root;
}
int main(){
	int tt;
	cin>>tt;
	for(int l=1;l<=tt;l++){
		int fa=0;
		n=read(),m=read();
		for(int i=0;i<=n;i++){
			f[i]=i;
			d[i]=0;
		}
		for(int i=1;i<=m;i++){
			int x,y;
			scanf("%d %d",&x,&y);
			
			if(fa)continue;
			
			int fx=find(x);
			int fy=find(y);
			if(fx==fy){
				if(d[x]==d[y]){
					fa=1;
				}
			}
			else{
				f[fy]=fx;
				d[fy]=d[x]^d[y]^1;
			}
		}
		if(fa){
			printf("Scenario #%d:\n",l);
			printf("Suspicious bugs found!\n\n");
		}
		else{
			printf("Scenario #%d:\n",l);
			printf("No suspicious bugs found!\n\n");
		}
	}
	return 0;
}

11.POJ 2912 Rochambeau

本質上也跟食物鏈是一樣的,根據> <符號判斷兩個人之間的關係,存入結構體,對每一個人進行判斷,如果沒有這個人是否會使其他人的輪次不合邏輯,如果有,存下這個人編號並break,最後判斷有多少這樣的judge,如果大於等於2,則無法判斷誰纔是judge,如果只有一人,則輸出最後一個判斷出邏輯錯誤的事件編號

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int f[5005],d[5005],pos[5005];
int n,m;
struct node{
	int x,y;
	char ch;
}a[5005];
int find(int x){
	if(f[x]==x)return x;
	int root=find(f[x]);;
	d[x]+=d[f[x]];
	f[x]=root;
	return f[x];
}
bool hb(int x,int y,int z){
	int fx=find(x);
	int fy=find(y);
	if(fx==fy&&(d[x]-d[y]-z)%3)return false;
	else if(fx!=fy){
		f[fx]=fy;
		d[fx]=d[y]-d[x]+z;
	}
	return true;
}
int main(){
	while(cin>>n>>m){
		memset(pos,0,sizeof(pos));
		for(int i=1;i<=m;i++){
	    	scanf("%d%c%d",&a[i].x,&a[i].ch,&a[i].y);
	    	if(a[i].ch=='<')swap(a[i].x,a[i].y);
		}
		for(int i=0;i<n;i++){
			for(int j=0;j<n;j++){
	        	f[j]=j;
	        	d[j]=0;
        	}
			for(int j=1;j<=m;j++){
				int x=a[j].x,y=a[j].y;
				if(x==i||y==i)continue;
				if(!hb(x,y,a[j].ch!='=')){
					pos[i]=j;
					break;
				}
			}
		}
		int cnt=0,ans=0,id;
		for(int i=0;i<n;i++){
			if(!pos[i]){
				cnt++;
				id=i;
			}
			ans=max(ans,pos[i]);
		}
		if(cnt==0)printf("Impossible\n");
		else if(cnt>1)printf("Can not determine\n");
		else printf("Player %d can be determined to be the judge after %d lines\n", id, ans);
	}
	return 0;
}

12.ZOJ 3261 Connections in Galaxy War

這題最騷的一個點就是要刪邊,想破頭也不知道怎麼處理好
看了題解記錄用map每一個要刪的邊,不刪的先連起來,然後反向遍歷,遍歷到要刪的邊的時候再把它連起來,這樣就不會影響到後面的結果了
想出這個的真的不容易%%%
(提醒要注意反向的思想)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int n,m,k,f[50005],d[50005],a[500005],w[50005],num[50005],ans[50005];
int ok;
map<pair<int,int>,int>mp;
struct node{
	int x,y;
	char s[50];
}q[50005];
struct nodd{
	int x,y;
}e[50005];
int find(int x){
	if(x==f[x])return x;
	return f[x]=find(f[x]);
}
void hb(int x,int y){
	int fx=find(x);
	int fy=find(y);
	f[fy]=fx;
	if(w[fx]<w[fy]){
		w[fx]=w[fy];
		num[fx]=num[fy];
	}
	else if(w[fx]==w[fy]){
		num[fx]=min(num[fx],num[fy]);
	}
}
int main(){
	while(cin>>n){
		mp.clear();
		for(int i=0;i<n;i++){
			scanf("%d",&a[i]);
			f[i]=i;
			num[i]=i;
			w[i]=a[i];
		}
		cin>>m;
		for(int i=1;i<=m;i++){
			scanf("%d %d",&e[i].x,&e[i].y);
		}
		cin>>k;
		for(int i=1;i<=k;i++){
			scanf("%s",q[i].s);
			if(q[i].s[0]=='q')scanf("%d",&q[i].x);
			else{
				scanf("%d %d",&q[i].x,&q[i].y);
				mp[make_pair(q[i].x,q[i].y)]=1;
				mp[make_pair(q[i].y,q[i].x)]=1;
			}
		}
		for(int i=1;i<=m;i++){
			int x=e[i].x,y=e[i].y;
			if(!mp[make_pair(x,y)])hb(x,y);
		}
		int cnt=0;
		for(int i=k;i>=1;i--){
			int x=q[i].x,y=q[i].y;
			if(q[i].s[0]=='d')hb(x,y);
			else{
				int fx=find(x);
				if(w[fx]>a[x])ans[++cnt]=num[fx];
				else ans[++cnt]=-1;
			}
		}
		if(ok)cout<<endl;
		ok=1;
		for(int i=cnt;i>=1;i--)printf("%d\n",ans[i]);
	}
	return 0;
}

13.HDU 1272 小希的迷宮

我以爲這題正解要存邊點並記錄入並查集判斷最終是否所有點全部連通,並且邊數==點數-1,32mb內存限制直接MLE了,事實證明這個水數據不需要判斷是否連通,直接判斷點數和邊數就可以了,好無語…

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
int n,m;
int f[100005];
int find(int x){
	if(x==f[x])return x;
	return f[x]=find(f[x]);
}
struct edge{
	int u,v;
};
void hb(int x,int y){
	int fx=find(x);
	int fy=find(y);
	f[fx]=fy;
}
set<int>s;
//vector<int>d;
int main(){
	while(cin>>n>>m){
		if(n==-1&&m==-1)break;
		if(n==0){
			printf("Yes\n");
			continue;
		}
	//	for(int i=1;i<=100005;i++){
	//		f[i]=i;
	//	}
		s.clear();
	//	d.clear();
		int fa=0;
		int cnt=0;
		s.insert(n),s.insert(m),cnt++;
	//	d.push_back(n);
	//	d.push_back(m);
	//	hb(n,m);
		while(cin>>n>>m){
			if(m==0&&n==0)break;
			s.insert(n),s.insert(m),cnt++;
	//		d.push_back(n);
	  //  	d.push_back(m);
	  //  	hb(n,m);
		}
		if(s.size()-1==cnt)fa=1;
	//	for(int i=0;i<d.size();i++){
//			if(find(d[0])!=find(d[i]))fa=0;
//		}
		if(fa==1){
			printf("Yes\n");
		}
		else{
			printf("No\n");
		}
	}
	return 0;
}

14.POJ 1308 Is It A Tree?

跟上一題一模一樣
end.

被帶權並查集虐的自閉了好幾天
然後發現發現其實也沒有想象中那麼難(?)
核心思想就是處理每個點跟祖先的距離關係(?)去回答題目詢問的信息
所以說,被虐的時候不要放棄,要相信自己,每天都有新的自閉!奧利給!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章