zoukankan      html  css  js  c++  java
  • kuangbin专题五 并查集【从入门到熟练】【9+1题】

    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;
    }
    View Code

    HDU 3038 How Many Answers Are Wrong

    网上blog普遍都用向量的角度去理解,但我一直不懂并查集是怎么跟向量扯上关系的?

    其实其本质是逻辑,可以从偏移量的角度出发去理解。(套路是维护一个数组a[i]代表i与其祖先的某种关系(其实是与父节点的关系,经过路径压缩变成与祖先的关系))

    对于这题还有一个难点,那就是对于给的a和b中,a要减一。

    如果说 [1,5] = 3,[6,10] = 3

    那应当推出来[1,10]是6(1到5偏移3,5到10再偏移3),所以需要a减一。【因为[a,b]的和,可以看成是a-1偏移到a,a偏移到a+1, ... , b-1偏移到b】

    #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;
    }
    View Code

    POJ 1182 食物链

    赤裸裸的逻辑,比上一题简单。

    其实理解关系并查集,能理解两幅图就行了,第一种是当x和y不在一个集合的时候。(x所在的集合里互相关系都知道,y所在集合里互相关系也都知道,那怎么建立rootx与rooty之间的关系?)

    从偏移量的角度去想,x经过一些可量化的操作偏移到rootx,rootx偏移到rooty,rooty偏移到y,y再偏移到x,所有的偏移量加起来应该是0,因为又回到了原点。(这是逻辑,只不过画出来像向量的形式)

    那就是 x->rootx + rootx->rootx->rooty + rooty->y + y->x = 0

    化简得到 x->rootx + rootx->rooty - y->rooty - x->y = 0

    再化简 rootx->rooty = y->rooty + x->y - x->rootx

    即 relation[ rootx ] = relation[ y ] + d - relation[ x ]

    另一种情况是x和y已经在一个集合里了,验证目前信息与之前的冲不冲突

    x->root + root->y +y->x = 0

    x->root - y->root - x->y = 0

    即验证 ( d == relation[x]-relation[y] )

    那最后就在想这个【可量化的操作】该怎么量化就可以了,一般来说比较好想

    #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;
    }
    View Code

    POJ 1417 True Liars

    背包+进阶并查集

    按关系并查集做那么最后会得到多个集合,其中每个集合中每两个人的互相关系已知(比如一共sum个人,那与root关系为0的有sum1个,与root关系为1的有sum2个,满足sum=sum1+sum2)那可能有sum1个圣人,也可能有sum2个,我们不知道。但这个集合要么为圣人做出sum1个人头的贡献,要么做出sum2个人头的贡献。那就变成了x个集合,每个集合有s1,s2,看能不能从每个集合里都取一个数最终凑成p1。那这不就是变成了个背包问题吗?能不能从前x种商品里恰好凑出p1(每种商品二选一买一个)

    //#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;
    }
    View Code

    POJ 1733 Parity Game

    离散化搞一下,还要注意x-1的问题。一般跟区间 [l,r] 和有关的东西都要考虑 l 减一

    考验一点码力

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 1e4 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int par[maxn],relation[maxn];
    int find_root(int a){
        if( a==par[a] ) return par[a];
        int root = find_root(par[a]);
        relation[a] += relation[ par[a] ];
        relation[a]%=2;
        return par[a] = root;
    }
    
    //even浠h〃0锛宱dd浠h〃1
    int x[5010],y[5010];
    char a[5050][10];
    vector<int> p;
    map<int,int> mp;
    
    int main(){ 
        //绂绘暎鍖?
        //ios::sync_with_stdio(false);
        int n;
        while( scanf("%d",&n)!=EOF ){
            int m; scanf("%d",&m);
    
            p.clear(); mp.clear();
            for(int i=1;i<=m;i++) scanf("%d%d%s",x+i,y+i,a[i]);
    
            for(int i=1;i<=m;i++) p.push_back(x[i]-1),p.push_back(y[i]);
            sort(p.begin(),p.end());
            p.erase( unique(p.begin(),p.end()),p.end() );
            //for(int i=0;i<p.size();i++) cout<<p[i]<<" "; cout<<endl;
    
            for(int i=0;i<p.size();i++) mp[ p[i] ] = i+1;//绂绘暎鍖?
            for(int i=1;i<=p.size();i++) par[i]=i,relation[i]=0;
    
            int ans=m;
            for(int i=1;i<=m;i++){
                int rt1 = find_root( mp[x[i]-1] );
                int rt2 = find_root( mp[y[i]] );
                
                int odd=0; if( a[i][0]=='o' ) odd=1;
                //cout<<"!!!"<<endl;
                //cout<<x[i]-1<<" "<<mp[x[i]-1]<<endl<<y[i]<<" "<<mp[y[i]]<<endl<<rt1<<" "<<rt2<<" "<<odd<<endl;
                
                if( rt1!=rt2 ){
                    par[rt1] = rt2;
                    relation[rt1]=(odd+relation[ mp[y[i]] ]-relation[ mp[x[i]-1] ]+2)%2;
                }
                else{
                    int rel = (relation[ mp[x[i]-1] ]-relation[ mp[y[i]] ]+2)%2;
                    if( rel!=odd ){
                        ans=i-1;
                        break;
                    }    
                }
            }
            printf("%d
    ",ans);
        }
    
        return 0;
    }
    View Code

    POJ 1984 Navigation Nightmare

    乍一看好像没啥思路,其实很简单。

    将偏移量分成两个(南北 和 东西),再定义个正方向,记录就好了

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 4e4 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int par[maxn],relation1[maxn],relation2[maxn];//偏移量有两个维度
    int from[maxn],to[maxn],len[maxn];
    char dir[maxn];
    vector< pair<int,int> > query[maxn];
    
    int find_root(int a){
        if( a==par[a] ) return a;
        int root = find_root(par[a]);
        relation1[a] += relation1[ par[a] ];
        relation2[a] += relation2[ par[a] ];
        return par[a] = root; 
    }
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int n,m; 
        while( scanf("%d%d",&n,&m)!=EOF){
            for(int i=1;i<=n;i++) par[i]=i;
            memset(relation1,0,sizeof(relation1));
            memset(relation2,0,sizeof(relation2));
            for(int i=1;i<=m;i++) query[i].clear();
    
            for(int i=1;i<=m;i++) scanf("%d%d%d %c",from+i,to+i,len+i,dir+i);
    
            int k; scanf("%d",&k);
            for(int i=1;i<=k;i++){
                int x,y,index; scanf("%d%d%d",&x,&y,&index);
                query[index].push_back( make_pair(x,y) );
            } 
    
            for(int i=1;i<=m;i++){
                //先把路加进去
                //N S W E
                int rt1 = find_root(from[i]);
                int rt2 = find_root(to[i]);
                par[ rt1 ] = rt2;
                int len1,len2;
                if( dir[i]=='S' ) len1=-len[i],len2=0;
                if( dir[i]=='N' ) len1=len[i],len2=0;
                if( dir[i]=='W' ) len1=0,len2=-len[i];
                if( dir[i]=='E' ) len1=0,len2=len[i];
                
                /*cout<<"!!!"<<endl;
                cout<<from[i]<<" "<<to[i]<<" "<<endl;
                cout<<relation1[to[i]]<<" "<<len1<<" "<<relation1[from[i]]<<endl;
                cout<<relation2[to[i]]<<" "<<len2<<" "<<relation2[from[i]]<<endl;*/
                relation1[ rt1 ] = relation1[ to[i] ] + len1 - relation1[ from[i] ];
                relation2[ rt1 ] = relation2[ to[i] ] + len2 - relation2[ from[i] ];
                
                //cout<<relation1[rt1]<<" "<<relation2[rt1]<<endl;
                //处理询问
                for(int j=0;j<query[i].size();j++){
                    int x = query[i][j].first, y = query[i][j].second;
                    int rootx = find_root(x), rooty = find_root(y);
                    if( rootx!=rooty ) printf("-1
    ");
                    else{
                        int ans= abs(relation1[x]-relation1[y])+abs(relation2[x]-relation2[y]);
                        printf("%d
    ",ans);
                    } 
                }
    
            }
        }
    
        return 0;
    }
    View Code

    POJ 2492 A Bug's Life

    这么水的题应该放到前面去啊

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 2e3 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int par[maxn],relation[maxn];
    
    int find_root(int a){
        if( a==par[a] ) return a;
        int root = find_root(par[a]);
        relation[a] += relation[ par[a] ];
        relation[a]%=2;
        return par[a] = root; 
    }
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int t,tc=0; scanf("%d",&t);
        while(t--){
            int n,m; scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++) par[i]=i;
            memset(relation,0,sizeof(relation));
            
            bool flag=true;
            for(int i=1;i<=m;i++){
                int x,y; scanf("%d%d",&x,&y);
                if( !flag ) continue;
                int rt1 = find_root(x),rt2 = find_root(y);
                if( rt1==rt2 ){
                    if( (relation[x]-relation[y]+2)%2!=1 ) flag=false;
                }
                else{
                    par[rt1]=rt2;
                    relation[rt1] = ( relation[y]+1-relation[x] )%2;
                }
            }
    
            printf("Scenario #%d:
    ",++tc);
            if( flag ) printf("No suspicious bugs found!
    ");
            else printf("Suspicious bugs found!
    ");
    
            printf("
    ");
            
        } 
        
    
        return 0;
    }
    View Code

    POJ 2912 Rochambeau

    我原本以为并查集就不能暴力了,哈哈哈,结果这道题要暴力一下,我服了哈哈哈

    脑回路比较清奇

    与其在发生冲突的时候想哪个人可能是coach(实际上是想不到的,因为集合里的人都可能是),他这个是枚举每个人是coach的情况。

    那么如果你是个合法的coach的话,那就把有你参加的round全部扔掉,然后看其他的是不是自洽。(就是食物链有木有!)

    如果出现多个合法coach那就can't determine;如果没有合法coach那就impossible;如果一个coach,那就说明其他人是coach的时候都会不合法,看其他人最晚是在哪个line都被淘汰的。帅的么

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 5e2 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int par[maxn],relation[maxn];
    int find_root(int a){
        if( a==par[a] ) return a;
        int root = find_root( par[a] );
        relation[a]+=relation[par[a]];
        relation[a]%=3;
        return par[a]=root;
    }
    
    int x[2010],y[2010],d[2010];
    //小于是1
    //相等是0
    //大于是2
    
    int main(){ 
        //离散化
        //ios::sync_with_stdio(false);
        int n,m;
        while( scanf("%d%d",&n,&m)!=EOF ){
            if(m==0){
                if(n==1) printf("Player 0 can be determined to be the judge after 0 lines
    ");
                else printf("Can not determine
    ");
                continue;
            }
    
            for(int i=1;i<=m;i++){
                char op; scanf("%d%c%d",x+i,&op,y+i);
                if( op=='<' ) d[i]=1;
                else if(op=='=') d[i]=0;
                else d[i]=2;
            }
    
            int cnt=0,coach;//多少个合法coach
            int last=0;//最后的冲突line
            for(int i=0;i<n;i++){
                memset(relation,0,sizeof(relation));
                for(int j=0;j<n;j++) par[j]=j;
                
                for(int j=1;j<=m;j++){
                    if( x[j]==i || y[j]==i ){
                        if(j==m) { cnt++; coach=i; }
                        continue;
                    }
    
                    int rootx = find_root(x[j]);
                    int rooty = find_root(y[j]);
                    if( rootx!=rooty ){
                        par[rootx]=rooty;
                        relation[rootx] = (d[j]+relation[y[j]]-relation[x[j]]+3)%3;
                    }
                    else{
                        int rel = (relation[x[j]]-relation[y[j]]+3)%3;
                        if( rel!=d[j] ) { last=max(last,j); break; }
                    }
    
                    if(j==m) { cnt++; coach=i; }//有了合法coach
                }
            }
            if( cnt==0 ) printf("Impossible
    ");
            else if(cnt==1) printf("Player %d can be determined to be the judge after %d lines
    ",coach,last);
            else printf("Can not determine
    ");
    
        }
    
        return 0;
    }
    View Code

    ZOJ 3261 Connections in Galaxy War

    这道题也好骚啊,我一开始看要做逆操作直接懵了。

    实际上思路也确实很妙啊,离线一下反向回答询问就行了

    #include<stdio.h>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 1e4 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int par[maxn],p[maxn];
    int find_root(int a){
        if( a==par[a] ) return a;
        return par[a]=find_root( par[a] );
    }
    
    int x[2*maxn],y[2*maxn];
    int op[5*maxn],i1[5*maxn],i2[5*maxn];//1是询问,0是摧毁
    map<int,int> mp[maxn];
    vector<int> ans;
     
    int main(){ 
        //离散化
        //ios::sync_with_stdio(false);
        int n;
        bool first=true;
        while( scanf("%d",&n)==1 ){
            if(first) first=false;
            else printf("
    ");
    
            ans.clear();
            for(int i=0;i<n;i++) scanf("%d",p+i);
            int m; scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d%d",x+i,y+i);
            for(int i=0;i<=n;i++) mp[i].clear();
            for(int i=0;i<=n;i++) par[i]=i;
            
            int q; scanf("%d",&q);
            for(int i=1;i<=q;i++){
                char a[10]; scanf("%s",a);
                if( a[0]=='q' ) op[i]=1;
                else op[i]=0;
    
                if(op[i]) scanf("%d",i1+i);
                else {
                    scanf("%d%d",i1+i,i2+i);
                    mp[ i1[i] ][ i2[i] ] = 1; mp[ i2[i] ][ i1[i] ]=1;
                }
            //    cout<<"!!!"<<a<<" "<<op[i]<<" "<<i1[i]<<" "<<i2[i]<<endl;
            }
            //把所有未摧毁的tunnel连起来
            for(int i=1;i<=m;i++){
                if( mp[x[i]][y[i]] ) continue;
                int rootx = find_root(x[i]);//x[i]所能联系到的最大星系
                int rooty = find_root(y[i]);
                if( p[rootx]==p[rooty] ){//用编号小的
                    if(rootx<=rooty) par[rooty]=rootx;
                    else par[rootx]=rooty;
                }
                else if( p[rootx]<p[rooty] ) par[rootx] = rooty;
                else if( p[rootx]>p[rooty] ) par[rooty] = rootx;
            }
    
            for(int i=q;i>=1;i--){//逆向处理所有操作
                if( op[i] ){//询问
                    int root = find_root(i1[i]);
                    if( p[root]>p[i1[i]] ) ans.push_back(root);
                    else ans.push_back(-1);
                }
                else{
                    int rootx = find_root(i1[i]);//x[i]所能联系到的最大星系
                    int rooty = find_root(i2[i]);
                    if( p[rootx]==p[rooty] ){//用编号小的
                        if(rootx<=rooty) par[rooty]=rootx;
                        else par[rootx]=rooty;
                    }
                    else if( p[rootx]<p[rooty] ) par[rootx] = rooty;
                    else if( p[rootx]>p[rooty] ) par[rooty] = rootx;
                }
            }
    
            for(int i=ans.size()-1;i>=0;i--) printf("%d
    ",ans[i]);
        }
    
        return 0;
    }
    View Code

    POJ 1308 Is It A Tree?

    就是并查集在找环时的应用,再考虑一下自环和空树就行了。

    代码写的比较丑

    #include<stdio.h>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 1e5 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int par[maxn],vis[maxn];
    int find_root(int a){
        if( a==par[a] ) return a;
        return par[a]=find_root( par[a] );
    }
    
    int main(){ 
        //离散化
        //ios::sync_with_stdio(false);
        int tc=0;
        while(1){
            memset(vis,0,sizeof(vis));
            int num=0,len=0;
            for(int i=1;i<=maxn-10;i++) par[i]=i;
            
            int l,r; scanf("%d%d",&l,&r);
            if(l==-1 && r==-1) break;
            if(l==0 && r==0) { printf("Case %d is a tree.
    ",++tc); continue; }
            if(l==r) { vis[l]=1; num=1; len=1; }
            else{
                par[l]=r; 
                vis[l]=vis[r]=1;
                num=2; len=1;
            }
            bool flag=true;//回答yes
            while(1){
                int x,y; scanf("%d%d",&x,&y);
                if(x==0 && y==0) break;
                if(!flag) continue;
    
                int rootx = find_root(x);
                int rooty = find_root(y);
                if(rootx!=rooty){
                    par[rootx]=rooty;
                    if(!vis[x]) { num++; vis[x]=1; }
                    if(!vis[y]) { num++; vis[y]=1; }
                    len++;
                }
                else flag=false;
            }
            if(flag){
                if( len==num-1 ) printf("Case %d is a tree.
    ",++tc);
                else printf("Case %d is not a tree.
    ",++tc);
            }
            else printf("Case %d is not a tree.
    ",++tc);
        }
    
        return 0;
    }
    View Code

    NOIP2010 关押罪犯

    用并查集的情况下有两种做法,难的做法是对第i个囚犯再对应一个点为i+n,那么与i+n在一个集合里的人都不跟i在一个监狱(画画图就能理解了),这样就巧妙的解决了即便知道两个罪犯要分开,但不知道哪个罪犯进哪个监狱的问题;

    另一种做法就超简单,用关系并查集,1代表不在一个监狱,0代表在一个监狱,然后一直做到冲突为止

    //#include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
    #include<vector>
    #define inf 2e9
    #define maxnode 200000
    #define ll long long
    #define lowbit(x) (x&(-x))
    const int mod = 10007;
    const int maxn = 2e4 + 10;
    int dx[4]={0,0,1,-1};
    int dy[4]={1,-1,0,0};
    using namespace std;
    
    int par[maxn],relation[maxn];
    struct node{ int x,y,v; }nodes[100000+10];
    bool cmp(node n1,node n2){
        return n1.v>n2.v;
    }
    
    int find_root(int a){
        if( a==par[a] ) return a;
        int root = find_root(par[a]);
        relation[a] += relation[ par[a] ];
        relation[a]%=2;
        return par[a] = root; 
    }
    
    int main(){ 
        //ios::sync_with_stdio(false);
        int n,m; scanf("%d%d",&n,&m);
    
        for(int i=1;i<=n;i++) par[i]=i;
        memset(relation,0,sizeof(relation));
            
        for(int i=1;i<=m;i++) scanf("%d%d%d",&nodes[i].x,&nodes[i].y,&nodes[i].v);
        sort(nodes+1,nodes+1+m,cmp);
    
        //1不在一个监狱,0在一个监狱
        for(int i=1;i<=m;i++){
            int x=nodes[i].x,y=nodes[i].y;
            int rt1 = find_root(x),rt2 = find_root(y);
            if( rt1==rt2 ){
                if( (relation[x]-relation[y]+2)%2!=1 ) { printf("%d
    ",nodes[i].v); return 0; }
            }
            else{
                par[rt1]=rt2;
                relation[rt1] = ( relation[y]+1-relation[x] )%2;
            }
        }
        
        printf("0
    ");
    
        return 0;
    }
    View Code
  • 相关阅读:
    AMH4.2 Ftp账号路径修改设置
    过狗一句话
    破解tumblr背景音乐
    lnmp下安装ffmpeg和ffmpeg-php教程
    How To install FFMPEG, FLVTOOL2, MP4Box on CentOS server 2015 easy method
    自己的路删除
    弹出CPA
    JSON的相关知识
    JavaScript函数的相关知识
    JavaScript对象的相关知识
  • 原文地址:https://www.cnblogs.com/ZhenghangHu/p/10030025.html
Copyright © 2011-2022 走看看