1. 程式人生 > >美團2018年CodeM大賽-複賽-C-邊的染色(技巧題->爆搜)

美團2018年CodeM大賽-複賽-C-邊的染色(技巧題->爆搜)

連結:https://www.nowcoder.com/acm/contest/152/C
來源:牛客網

連結: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;
}