CF553C Love Triangles(带权并查集)

维护集合关系,可以用拓展域并查集,2sat,带权并查集

其中带权并查集难理解(不过代码短,个人认为

2sat和并查集的主要区别在于,并查集可以维护两者是否属于一类的问题,而2sat相比于其维护的范围更广一点,可以带逻辑的推理p->q,非p->q之类的...

以下用带权并查集..

CF553C Love Triangles

给出n个点,要求构造合法的完全图,已经给出了一些边。

边有爱边和恨边,其中任意三个点,连成的边合法的组合有爱爱爱,爱恨恨。

问符合要求的完全图的数量,对1e9+7取模.n<=1e5

思路:

显然对于三个点,若已知两边则第三边也确定了。如果已知一边,则其他两边有两种选择

不妨设爱的边权为0,恨为1,那么符合的三个点之间三条边的异或值为0。

我们把点连好后,输入完,整张图就是若干连通块(暂不考虑任意三点的条件

我们把连通块缩点,我们考虑任意两个极大连通块的关系,设块1位s1,块2为s2,假设现在加一条边使得,s1s2连通,则对于任意u∈s1,v∈s2,u和v的关系就确定下来了(假设在xy间加边,那么在s1s2中和xy相连的点都可以慢慢确定下来,已知2推1),换句话就是说,任意两个极大连通块间有两种连法,假设缩点后有m个块,那么答案就是2^(m-1)

接下来考虑维护这个边权01的正确性

考虑给每个点赋值,若边权为0,则u=0,v=0 / u=1,v=1,进一步边权则代表两点是否属于一类

我们用带权并查集维护集合内点与根是否属于一类,这个信息(0一类,1不是一类

输入的时候把边权反一下

假设我们现在要合并uv,若uv在同一集合内,显然uv与root组成的三元环异或值为0,如果不是则不合法

若不在一集合内,根据传递性,合并的时候,root_u -> u -> v -> root_v ,因为异或是无向的,所以root_u与root_v的新边的值就是uv和输入的边权z的异或和

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cstring>
#include<string>
#include<algorithm>
#include<math.h>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<deque>
#include<set>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
const int N=1e5+10;
const int mod=1e9+7;
const double eps=1e-8;
int n,m;
int fa[N],g[N];
int find(int x)
{
    if( fa[x] == x)  return x;
    int t = find(fa[x]);
    g[x] ^= g[fa[x]];
    return fa[x] = t;
}
ll qsm(int a,int b  )
{
    int ans=1,temp=a;
    while( b )
    {
        if( b&1 ) ans = (ans * temp )%mod;
        temp = (temp * temp)%mod;
        b>>=1;
    }
    return ans;
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    IOS;
    cin>>n>>m;
    _for(i,1,n) fa[i]=i;
    _for(i,1,m)
    {
        int x,y,z;cin>>x>>y>>z;
        int fx = find(x);
        int fy = find(y);
        int temp = g[x]^g[y];
        z^=1;
        if( fx!=fy )
        {
            fa[fx] = fy;
            g[fx] = g[x]^g[y]^z;
        }
        else//如果是一类,x,y,fy三元环异或为0
        {
            if( temp^z ) return cout<<0<<endl,0;
        }
    }
    int tot=0;
    _for(i,1,n)
    {
        if( i==find(i) ) tot++;
    }
    int ans = qsm(2,tot-1);
    cout<<ans<<endl;
    AC;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值