題目鏈接
題意:給定n個點,給出一些邊權為0/1的邊,構造完全圖,滿足對於任何一個三元環,三條邊權和為奇。求符合條件的完全圖數量,對\(1e9+7\)取模。
分析:其實原題給定的邊權是love/hate,love即1,hate即0。
所以對於三元環而言,只存在“愛愛愛”或“愛恨恨”。
如果我們按此討論點與點之間的關系,我們會想到什么?
敵人的敵人就是朋友,朋友的朋友還是朋友。
那么這道題就和[BOI2003]團伙的描述有些類似了。
我們顯然可以用到並查集。
團伙那題,我們確定兩點關系可以建立補集,也可以使用帶權並查集。
這題,我們發現,一個集合是否有補集在統計答案時並無差別(都相當於一個點),所以我們使用帶權並查集。
帶權並查集的方法就十分顯然了,對於love的邊,連一條權值為0的邊,對於hate的邊,連一條權值為1的邊(注意與題目所給的相反)。每次連邊是順便檢查兩點關系。
最后我們得到\(k\)個集合,把\(k\)個集合放入兩個桶中,有\(2^k\)種方法,再去重,最后的答案就是\(2^{k-1}\)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int fa[100100], val[100100];//fa是所在集合,val是與祖先關系
inline int read()// Fast input
{
int x=0,f=1; char ch=getchar();
for (; ch<'0' || ch>'9'; ch=getchar()) if (ch=='-') f=-1;
for (; ch>='0' && ch<='9'; ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
int find(int x)//帶權並查集
{
if (fa[x]==x) return x;
int t=find(fa[x]);
val[x]^=val[fa[x]];
return fa[x]=t;
}
int main()
{
int n=read(), k=read();
for (int i=1; i<=n; i++) fa[i]=i;
for (int i=1; i<=k; i++)
{
int u=read(), v=read(), w=read()^1;
int fu=find(u), fv=find(v);
if (fu!=fv)
{
fa[fv]=fu;
val[fv]=val[u]^val[v]^w;
}
else
{
if (w && !(val[u]^val[v])) {puts("0"); return 0;}
if (!w && val[u]^val[v]) {puts("0"); return 0;}
}
}
int res=0, ans=1;
for (int i=1; i<=n; i++) if (find(i)==i) res++;
for (int i=1; i<=res-1; i++) ans=(ans*2)%mod;//(其實可以寫quick_power的qwq
printf("%d\n",ans);
return 0;
}