美團2018年CodeM大賽-複賽-C-邊的染色(技巧題->爆搜)
阿新 • • 發佈:2019-02-10
連結:https://www.nowcoder.com/acm/contest/152/C
來源:牛客網
連結:https://www.nowcoder.com/acm/contest/152/C
來源:牛客網
現在你需要給剩下的邊標記邊權為0或1,求有幾種標記的方式滿足:
對於G中任意一個環,裡面所有邊的邊權的異或值為0。 環的定義如下: 對於任意k(k≥2)個點{a1,a2,...,ak},若對於所有的i<k滿足ai與ai+1之間有邊,且a1=ak成立,則這k個點構成一個環。
來源:牛客網
連結:https://www.nowcoder.com/acm/contest/152/C
來源:牛客網
題目描述
小團有一張n個點,m條邊的無向圖G,有些邊上已經被標記了0或1,表示它的邊權。現在你需要給剩下的邊標記邊權為0或1,求有幾種標記的方式滿足:
對於G中任意一個環,裡面所有邊的邊權的異或值為0。 環的定義如下: 對於任意k(k≥2)個點{a1,a2,...,ak},若對於所有的i<k滿足ai與ai+1之間有邊,且a1=ak成立,則這k個點構成一個環。
輸入描述:
第一行兩個整數n, m。 接下來m行,每行3個整數u, v, w,表示有一條邊(u,v),若w=-1則這條邊上沒有標記,否則w=0或1表示這條邊上標記了w。 資料保證沒有重邊和自環。 1≤n≤100,000 0≤m≤min(n*(n-1)/2, 100000)
輸出描述:
輸出方案數對998,244,353取模的值。
題解:首先判斷非法情況,也就是一個環上所有邊都已經標記過了,並且所有的權值異或起來為1,此時我們可以考慮先將所有標記的邊建成一張圖,然後用類似於二染色的方法判斷是否是非法情況。
對於合法情況,在一個環中根據邊來找合法情況是困難的,於是我們要考慮如何將邊權轉化為點的權值,我們可以想到,假如當前一個環中所有的邊都沒有被標記,呢情況數是多少呢?
我們考慮這樣一種做法(聽室友講,是一場區域賽題的解法),對於每個邊的權值,將其轉化為這條邊的兩個端點的異或值,因為這樣做的話在一個環中每個點肯定異或兩次!!! 是不是完美解決了上邊的問題,呢答案不就是2^(3-1)=4了,為啥是3-1呢,你要知道對於當前的一種邊權的狀態,點權肯定有兩種情況(所有點的權值取反即可)。。。
然後回到這道題,無非就是多了一些已經有標記的邊,但是仔細想想會發現,這些已標記的邊所在的聯通塊中的所有點權其實都是確定的。。。。想到這裡答案就已經出來了。
呢最後答案無非就是2^(當前聯通塊大小)的乘積/(2^(已標記邊的聯通塊大小))
#include<vector> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define ll long long #define mod 998244353 #define maxn 100005 ll ans=1; int n,m,a[maxn],b[maxn],c[maxn],vis[maxn],flag[maxn],mark; struct node { int v,c; }; vector<node>q[maxn]; ll qq(ll x,ll y) { ll res=1; while(y) { if(y%2) res=res*x%mod; x=x*x%mod; y/=2; } return res; } void dfs1(int x) { if(mark) return;vis[x]=1; if(flag[x]==-1) flag[x]=1; for(int i=0;i<q[x].size();i++) { int v=q[x][i].v; if(flag[v]==-1) flag[v]=(q[x][i].c^flag[x]); else { if((flag[x]^flag[v])!=q[x][i].c) mark=1; } if(vis[v]) continue; dfs1(v); } } ll dfs2(int x) { ll tmp=0;vis[x]=1; for(int i=0;i<q[x].size();i++) { int v=q[x][i].v; if(vis[v]) continue; tmp+=dfs2(v); } return tmp+1; } int main(void) { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&a[i],&b[i],&c[i]); if(c[i]!=-1) { node tmp;tmp.v=b[i];tmp.c=c[i]; q[a[i]].push_back(tmp); tmp.v=a[i];tmp.c=c[i]; q[b[i]].push_back(tmp); } } memset(flag,-1,sizeof(flag)); for(int i=1;i<=n;i++) if(!vis[i]) dfs1(i); if(mark) { printf("0\n"); return 0; } for(int i=1;i<=m;i++) { if(c[i]!=-1) continue; node tmp;tmp.v=b[i];tmp.c=c[i]; q[a[i]].push_back(tmp); tmp.v=a[i];tmp.c=c[i]; q[b[i]].push_back(tmp); } memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) { if(vis[i]) continue; else { ll sum=dfs2(i); ans=(ans*qq(2ll,sum-1)%mod)%mod; } } for(int i=1;i<=n;i++) q[i].clear(); for(int i=1;i<=m;i++) { if(c[i]!=-1) { node tmp;tmp.v=b[i];tmp.c=c[i]; q[a[i]].push_back(tmp); tmp.v=a[i];tmp.c=c[i]; q[b[i]].push_back(tmp); } } memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) { if(vis[i]) continue; else { ll sum=dfs2(i); ans=(ans*qq(qq(2,sum-1)%mod,mod-2)%mod)%mod; } } printf("%lld\n",ans); return 0; }