Kattis - dancecircle —— 带权并查集

This way

题意:

n个点围成一个圈,每个点的值要么是0,1.告诉你每个点左li到右边ri区间1的个数是奇数个还是偶数个,问你这个01串有多少种组成方法。

题解:

上一次做带权并查集还是在两年前,,现在已经差不多忘掉了。
带权并查集它的权值指的是从这个点到根节点的差值,那么在find的时候

int finds(int x){
    if(fa[x]<0)
        return x;
    int f=fa[x];
    fa[x]=finds(fa[x]);
    bit[x]^=bit[f];
    return fa[x];
}

是先计算父亲节点到根节点的东西,再将这个点和父节点进行运算。
然后对于合并来说,有两种情况,一个是fax合并到fay上,还有一种则是相反,它们的权值赋予是有区别的:
在这里插入图片描述
如果是fax合并到fay上,那么v[fax]的权值就是:
v[fax]+v[x]=v[y]+s
v[fax]=-v[x]+v[y]+s
那么如果是fay合并到fax上,v[fay]的权值就是
v[fay]+v[y]+s=v[x]
v[fay]=v[x]-v[y]-s
对于这道题来说,都是异或。
那么我们只需要考虑整个区间的异或值是奇数还是偶数即可,那么对于一个区间,我们就知道它的补区间是奇数还是偶数,这样的话我们就不需要将它看成一个圆,而是1-n的区间,告诉你n个区间的奇偶性,让你找。
但是要注意,每个区间的左端点要往左移一格,因为如果1-2区间和3-4区间已知,那么1-4区间就已知,它是端点权值,所以要合并的话,需要将左端点左移。
那么第一种情况就是0-n区间是偶数个1,那么枚举每个已知区间,如果这两个端点之前不再一个并查集里面,就合并并且将num–,num记录的是可能性,因为如果一个区间确定了奇偶性的话,就失去了另外一种可能性。
然后如果这两个点已经合并,并且它们在并查集中的值和告诉你的值有悖的话,那就不可能。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int N=2e5+5;
int fa[N],bit[N];
int finds(int x){
    if(fa[x]<0)
        return x;
    int f=fa[x];
    fa[x]=finds(fa[x]);
    bit[x]^=bit[f];
    return fa[x];
}
int merge(int x,int y,int b){
    int fax=finds(x),fay=finds(y);
    if(fax!=fay){
        if(fa[fax]>fa[fay])//启发式合并
            swap(fax,fay);
        fa[fax]+=fa[fay];
        fa[fay]=fax;
        bit[fay]=bit[x]^bit[y]^b;
        return 1;
    }
    if(bit[x]^bit[y]^b)
        return 0;
    return 2;
}
int l[N],r[N],odd[N];
ll qpow(ll a,ll b){ll ans=1;for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;return ans;}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&l[i],&r[i],&odd[i]),l[i]=(i-l[i]+n-1)%n,r[i]=(i+r[i]-1)%n+1;
    memset(fa,-1,sizeof(fa));
    //0-n are even
    merge(0,n,0);
    int num=n-1,f=0;
    ll ans=0;
    for(int i=1;i<=n;i++){
        int cas;
        if(l[i]>=r[i])
            cas=merge(r[i],l[i],odd[i]);
        else
            cas=merge(l[i],r[i],odd[i]);
        if(cas==1)
            num--;
        if(cas==0){
            f=1;
            break;
        }
    }
    if(!f)
        ans=qpow(2,num);
    //0-n are odd
    memset(fa,-1,sizeof(fa));
    memset(bit,0,sizeof(bit));
    merge(0,n,1);
    num=n-1,f=0;
    for(int i=1;i<=n;i++){
        int cas;
        if(l[i]>=r[i])
            cas=merge(r[i],l[i],odd[i]^1);
        else
            cas=merge(l[i],r[i],odd[i]);
        if(cas==1)
            num--;
        if(cas==0){
            f=1;
            break;
        }
    }
    if(!f)
        ans=(ans+qpow(2,num))%mod;
    printf("%lld\n",ans);
    return 0;
}

发布了584 篇原创文章 · 获赞 33 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/tianyizhicheng/article/details/104766939