kuangbin专题五 并查集从入门到熟练4题
Posted zhenghanghu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kuangbin专题五 并查集从入门到熟练4题相关的知识,希望对你有一定的参考价值。
POJ 1611 The Suspects
基础并查集,由于需要知道数量维护一个total就可以了
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<string> #include<stack> #include<fstream> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 10007; const int maxn = 3e4 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int par[maxn],total[maxn];//total[i]只对root有效,代表这个关系群的人数总数量 int get_root(int a){ if( par[a]==a ) return a; return par[a]=get_root( par[a] ); } void merge(int a,int b){ int root_a=get_root(a); int root_b=get_root(b); if( root_a==root_b ) return; par[ root_a ] = root_b; total[root_b]+=total[root_a]; } bool query(int a,int b){ return get_root(a)==get_root(b); } int main(){ while(1){ int n,m; scanf("%d%d",&n,&m); if(n==0 && m==0) break; for(int i=1;i<=n;i++) par[i]=i; for(int i=1;i<=n;i++) total[i]=1; for(int i=1;i<=m;i++){ int k; scanf("%d",&k); int start; scanf("%d",&start); start++; for(int i=1;i<k;i++){ int x; scanf("%d",&x); x++; merge(start,x); } } printf("%d ",total[get_root(1)] ); } return 0; }
HDU 3038 How Many Answers Are Wrong
网上blog普遍都用向量的角度去理解,但我一直不懂并查集是怎么跟向量扯上关系的?
其实其本质是逻辑,可以从偏移量的角度出发去理解。
进阶并查集,套路是维护一个数组a[i]代表i与其祖先的某种关系(其实是与父节点的关系,经过路径压缩普遍变成与祖先的关系)
#include<bits/stdc++.h> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 10007; const int maxn = 2e5 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int sum[maxn],par[maxn]; int find_root(int a){ if( par[a]==a ) return par[a]; int root = find_root(par[a]); sum[a]+=sum[ par[a] ];//得先改sum par[a]=root; return root; } int main(){ //freopen("1.in","r",stdin); int n,m; while( scanf("%d%d",&n,&m)!=EOF ){ int cnt=0; for(int i=0;i<=n;i++){ par[i]=i; sum[i]=0; } for(int i=1;i<=m;i++){ int a,b,v; scanf("%d%d%d",&a,&b,&v); a--; int roota=find_root(a); int rootb=find_root(b); if( roota==rootb ){ if( v!=sum[a]-sum[b] ) cnt++; } else{ par[roota]=rootb; sum[roota]=sum[b]-sum[a]+v; } } printf("%d ",cnt); } return 0; }
POJ 1182 食物链
赤裸裸的逻辑,比上一题简单
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<queue> #include<string> #include<stack> #include<fstream> #include<map> #include<iomanip> #include<algorithm> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 10007; const int maxn = 5e4 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int par[maxn],relation[maxn]; //relation[i]==0 代表 i跟par[i]是同类 //relation[i]==1 代表 i吃par[i] //relation[i]==2 代表 i被par[i]吃 int find_root(int a){//会顺手把par改掉,所以也要顺手把relation改掉 if( par[a]==a ) return par[a]; int root=find_root(par[a]); relation[a] = (relation[ par[a] ]+relation[a])%3; //a与root的关系,是par与root的关系加上a与par的关系 return par[a]=root; } void merge(int a,int b,int relat){//relat是 a->b是relat,b->a就不是了 int roota=find_root(a); int rootb=find_root(b); if( roota==rootb ) return; par[rootb]=roota; relation[rootb]=3-( (relat+relation[b]-relation[a]+3)%3); } int find_relation(int a){ if( par[a]==a ) return 0; return (relation[a]+find_relation(par[a]))%3; } int main(){ int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) par[i]=i; int cnt=0; for(int i=1;i<=k;i++){ int d,x,y; scanf("%d%d%d",&d,&x,&y); if( d==2 && x==y ) cnt++; else if( x>n || y>n ) cnt++; else{ if( d==1 ){ int rx=find_root(x); int ry=find_root(y); if( rx!=ry ) merge(x,y,0); else{ if( find_relation(x)==find_relation(y) ) continue; else cnt++; } } else{ int rx=find_root(x); int ry=find_root(y); if( rx!=ry ) merge(x,y,1); else{ if( (1+find_relation(y)-find_relation(x)+3)%3==0 ) continue; else cnt++; } } } } printf("%d ",cnt); return 0; }
POJ 1417 True Liars
背包+进阶并查集
明明想到了最后一步,但没能看出背包模型
//#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<vector> #include<cstring> #include<algorithm> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 10007; const int maxn = 1e3 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int par[maxn],relation[maxn];//relation[i]是i与祖先的关系 //0代表一样,1代表不一样 int find_root(int a){ if( par[a]==a ) return par[a]; int root = find_root(par[a]); relation[a] += relation[ par[a] ];//得先改sum relation[a]%=2; par[a]=root; return root; } int a[maxn][2];//a[i][0]代表第i个大集合中有多少个在一个集合,a[i][1]代表第i个大集合中有多少个在另一个集合 int used[maxn]; vector<int> b[maxn][2];//代表i大集合里的0所代表小集合里有谁 int dp[maxn][maxn],pre[maxn][maxn];//dp[i][j]代表前i个大集合凑出j个数的方案,答案是dp[cnt][p1] //pre[i][j]记录第i个大集合选了0还是1 int main(){ //freopen("1.in","r",stdin); int n,p1,p2,relate; while(1){ scanf("%d%d%d",&n,&p1,&p2); if( n==0 && p1==0 && p2==0 ) break; for(int i=1;i<=p1+p2;i++){ par[i]=i; relation[i]=0; } for(int i=1;i<=n;i++){ int x,y; scanf("%d%d",&x,&y); char s[10]; scanf("%s",s); if( s[0]==‘n‘ ) relate=1; else relate=0; int roota=find_root(x); int rootb=find_root(y); par[roota]=rootb; relation[roota]=(relation[y]-relation[x]+relate+2)%2; //cout<<"!!! "<<x<<" "<<y<<" "<<s<<" "<<relate<<endl; //cout<<x<<" "<<roota<<endl<<y<<" "<<rootb<<endl; } //现在分成了很多集合,每个集合又分成两个子集合,代表好人和坏人 memset(used,0,sizeof(used)); memset(a,0,sizeof(a)); for(int i=1;i<=1000;i++) b[i][0].clear(),b[i][1].clear(); int cnt=0; for(int i=1;i<=p1+p2;i++) if( !used[i] ){ cnt++; int tmp=find_root(i); for(int j=1;j<=p1+p2;j++)//遍历与i在一个大集合里的元素 if( tmp==find_root(j) ){ used[j]=1;//标记j为用过,这样大集合不会重复 a[cnt][ relation[j] ]++; b[cnt][ relation[j] ].push_back(j); } } /* for(int i=1;i<=cnt;i++){ cout<<"1"<<endl; for(int j=0;j<b[i][1].size();j++) cout<<b[i][1][j]<<" "; cout<<endl; cout<<"0"<<endl; for(int j=0;j<b[i][0].size();j++) cout<<b[i][0][j]<<" "; cout<<endl; cout<<i<<" "<<a[i][0]<<" "<<a[i][1]<<endl; }*/ //开始dp memset(dp,0,sizeof(dp)); memset(pre,0,sizeof(pre)); dp[0][0]=1; for(int i=1;i<=cnt;i++){ for(int j=0;j<=p1;j++){ if( j>=a[i][0] ) dp[i][j]+=dp[i-1][ j-a[i][0] ]; if( j>=a[i][1] ) dp[i][j]+=dp[i-1][ j-a[i][1] ]; if( dp[i][j]==1 ){ if( j>=a[i][0] && dp[i-1][j-a[i][0] ]==1 ) pre[i][j]=0; else pre[i][j]=1; } // cout<<"!!! "<<i<<" "<<j<<" "<<dp[i][j]<<" "<<pre[i][j]<<endl; } } if( dp[cnt][p1]==1 ){ vector<int> ans; ans.clear(); int id=cnt,p=p1; while( id ){ int choice=pre[id][p]; //cout<<id<<" "<<p<<endl; //cout<<"??? "<<choice<<endl; if( choice==1 ) for(int i=0;i<b[id][1].size();i++) ans.push_back( b[id][1][i] ); else for(int i=0;i<b[id][0].size();i++) ans.push_back( b[id][0][i] ); p-=a[id][choice]; id--; } sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++) printf("%d ",ans[i]); printf("end "); } else{ printf("no "); } } return 0; }
以上是关于kuangbin专题五 并查集从入门到熟练4题的主要内容,如果未能解决你的问题,请参考以下文章