codechef Parity tree(并查集加+LCA/LCT)

首先这题和树的形态没有任何关系。
考虑没有限制的情况,答案就是2^(n-1)次方(每条边可以赋0或1)。
再考虑有一个限制,u,v之间的路径的奇偶性要为x:
1.如果u,v之间为一条之间相连的路径,那么u,v的奇偶性直接被固定,答案除2。
2.如果u,v之间有多条边,显然我们可以在u,v直接选出一个点mid,我们任意构造u,mid这条路径的奇偶性,那么mid,v这条路径的奇偶性就被确定了,而在这之前,mid,v这条路径的奇偶性也是可以随意构造的,设之前mid,v这条路径可以构造的方案数为sum,那么在确定u,mid这条路径的奇偶性后,mid,v这条路径的总方案数就降为sum/2,根据乘法原理,总方案数被除以2。
PS:对于两个相邻边,有(0,0)(0,1)(1,0)(1,1)显然奇偶性为1和为0的方案数是相等的,所以确定了一遍之后这一边才减少一半的答案。

所以将问题转换,如果一条路被限制,那么边就会被添加一条限制,我们可以用并查集维护连通块的个数,每个连通块内的奇偶性,那么如果连通块的个数为k,答案就是2^(k-1)(一开始有n个连通块,少一个连通块就相当于答案除一次2,因为边数只有n-1,所以连通块个数还要减1)。

当然还有第三种情况:
3.添加u,v是,u,v之间的路径已经经过一系列限制得出了奇偶性。此时我们要判断w与之前的限制是否矛盾,那么只要找出u,v所在的环的边,判断w是否和环的答案矛盾即可。

这题可以直接LCA+并查集做,但是我懒得打代码直接复制板子用LCT做了(其实用了LCT就没必要用并查集了)。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,mod=1e9+7;
int n,m,a[N],t[N][2],fa[N],lz[N],s[N];
void init()
{
    
    
    memset(a,0,sizeof(a));
    memset(t,0,sizeof(t));
    memset(fa,0,sizeof(fa));
    memset(lz,0,sizeof(lz));
    memset(s,0,sizeof(s));
}
void pushup(int x)
{
    
    
    s[x]=(s[t[x][0]]+s[t[x][1]]+a[x])&1;
}
void update(int x){
    
    swap(t[x][0],t[x][1]);lz[x]^=1;}
void pushdown(int x)
{
    
    
    if(lz[x])
    {
    
    
        if(t[x][0]) update(t[x][0]);
        if(t[x][1]) update(t[x][1]);
        lz[x]=0;
    }
}
bool nroot(int x)
{
    
    
    return t[fa[x]][0]==x|t[fa[x]][1]==x;
}
void Rotate(int x)
{
    
    
    int y=fa[x],z=fa[y],k=(t[y][1]==x)^1;
    if(nroot(y)) t[z][t[z][1]==y]=x;
    t[y][k^1]=t[x][k];
    if(t[x][k]) fa[t[x][k]]=y;
    t[x][k]=y;
    fa[y]=x;
    fa[x]=z;
    pushup(y);
}
int st[N];
void splay(int x)
{
    
    
    int y=x,z=0;
    st[++z]=y;
    while(nroot(y)) st[++z]=y=fa[y];
    while(z) pushdown(st[z--]);
    while(nroot(x))
    {
    
    
        y=fa[x],z=fa[y];
        if(nroot(y)) Rotate((t[y][1]==x)^(t[z][1]==y)?x:y);
        Rotate(x);
    }
    pushup(x);
}
void access(int x)
{
    
    
    for(int y=0;x;x=fa[y=x])
    {
    
    
        splay(x);t[x][1]=y;pushup(x);
    }
}
void makeroot(int x)
{
    
    
    access(x);splay(x);update(x);
}
int findroot(int x)
{
    
    
    access(x);splay(x);
    while(t[x][0]) pushdown(x),x=t[x][0];
    splay(x);
    return x;
}
void link(int x,int y)
{
    
    
    makeroot(x);
    fa[x]=y;
}
void cut(int x,int y)
{
    
    
    makeroot(x);
    if(findroot(y)==x&&fa[y]==x&&!t[y][0])
    {
    
    
        fa[y]=t[x][1]=0;
        pushup(x);
    }
}
void split(int x,int y)
{
    
    
    makeroot(x);
    access(y);splay(y);
}
bool judge(int u,int v,int w)
{
    
    
    split(u,v);
    return s[v]==w;
}
int f[N],p[N];
int getf(int x){
    
    return f[x]==x?x:f[x]=getf(f[x]);}
int main()
{
    
    
    p[0]=1;
    for(int i=1;i<N;i++) p[i]=p[i-1]*2%mod;
    int t;scanf("%d",&t);
    while(t--)
    {
    
    
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<n;i++)
        {
    
    
            int u,v;scanf("%d%d",&u,&v);
        }
        bool flag=true;
        for(int i=1;i<=m;i++)
        {
    
    
            int u,v,w;scanf("%d%d%d",&u,&v,&w);
            int fu=getf(u),fv=getf(v);
            if(fu==fv)
            {
    
    
                if(flag&&!judge(u,v,w)) flag=false;
                continue;
            }
            f[fu]=fv;
            a[i+n]=w;
            link(u,i+n);link(i+n,v);
        }
        if(!flag) printf("0\n");
        else
        {
    
    
            int ans=0;
            for(int i=1;i<=n;i++)
                if(f[i]==i) ans++;
            printf("%d\n",p[ans-1]);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/Huah_2018/article/details/102556334