zoukankan      html  css  js  c++  java
  • codeforces1156D 0-1-Tree 并查集

    题目传送门

    题意:

      给定一棵n个点的边权为0或1的树,一条合法的路径(x,y)(x≠y)满足,从x走到y,一旦经过边权为1的边,就不能再经过边权为0的边,求有多少边满足条件?

    思路:

      首先这道题,换根dp也可以过(树形dp,点这里

      那么如何并查集做呢,我们考虑一个点$u$,我们将与u通过0边相连的连通点的数量记做$siz0$,将与u通过1边相连的连通点的数量记做$siz1$,那么所有符合条件的0-1边将u作为转折点的,0-0边将u作为终点的,1-1边将u作为终点的边的数量就等于$siz0*siz1-1$,减去的1就是自己连到自己。

      用并查集维护上面的信息,即$f[u][0]$表示u的0边祖先,$f[u][0]$表示u的1边祖先,然后$merge$即可。

    #pragma GCC optimize (2)
    #pragma G++ optimize (2)
    #pragma comment(linker, "/STACK:102400000,102400000")
    #include<bits/stdc++.h>
    #include<cstdio>
    #include<vector>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    #define dep(i,b,a) for(int i=b;i>=a;i--)
    #define clr(a,b) memset(a,b,sizeof(a))
    #define pb push_back
    #define pii pair<int,int >
    using namespace std;
    typedef long long ll;
    const int maxn=200010;
    ll rd()
    {
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int f[maxn][2],siz[maxn][2];
    int n,a,b,c;
    int find(int x,int y){
        if(!f[x][y]){
            return x;
        }
        return f[x][y]=find(f[x][y],y);
    }
    int main(){
        cin>>n;
        rep(i,1,n-1){
            scanf("%d%d%d",&a,&b,&c);
            int fx=find(a,c);
            int fy=find(b,c);
            if(fx!=fy){
                f[fx][c]=fy;
            }
        }
        rep(i,1,n){
            siz[find(i,0)][0]++,siz[find(i,1)][1]++;
        }
        ll ans=0;
        rep(i,1,n){
            ans+=1ll*siz[find(i,1)][1]*siz[find(i,0)][0]-1;
        }
        cout<<ans<<endl;
    }
  • 相关阅读:
    CF827D Best Edge Weight
    克鲁斯卡尔重构树总结
    模拟赛 提米树 题解 (DP+思维)
    luogu P4781 【模板】拉格朗日插值
    luogu P5826 【模板】子序列自动机
    子序列自动机
    luogu P1368 工艺 /【模板】最小表示法
    最小表示法
    SP1812 LCS2
    FZOJ 3602 T2
  • 原文地址:https://www.cnblogs.com/mountaink/p/11598291.html
Copyright © 2011-2022 走看看