AtCoder Regular Contest 093 E - Bichrome Spanning Tree

在這裏插入圖片描述

題意

給你一個圖,每條邊有一個邊權。
現在你可以對每條邊染色,染成黑色或白色。
一種合法的染色方案指的是染色完畢後,對原圖求MST(最小生成樹)。最小生成樹滿足所有樹上的邊至少包含一條黑色變和白色邊。然後最小生成樹的邊權和要等於給出的X值。
求合法染色方案數。

思考歷程

開始沒什麼想法。
然後我假設sum爲求一遍MST的值。
發現我們可以對x分成三種情況,一種是sum>xsum>x,顯然沒有情況。
一種是sum=xsum=x,這種情況推了老半天,推出來的似乎還錯了。
另外一種是sum<xsum<x,這個還是有個瓶頸。大致求出那些邊放進去後sum可以變成x。
然鵝其他影響的邊我就想不到了。
好蔡啊。

題解

看完題解就懂了。
其實再求一個dis(i)dis(i)表示當前邊放入生成樹後對答案的影響即可。
然後我就發現我全會了。
disdis值其實可以O(n log n)O(n\ log\ n)求,打個LCA即可。然鵝數據很水,暴力也可以。

sum=xsum=x的情況就是求出MST後,設gsgs表示disdis值爲0的邊的個數。
然後答案即爲:(2n1+gs2)(2mngs+1)(2^{n-1+gs}-2)*(2^{m-n-gs+1})
左邊:首先這些邊可以隨便染色,因爲無論怎麼組合它們組成的MST都是等於X的,減2表示這些邊不能全部染成黑色或白色。
右邊:無關的邊就隨便染色了。

sum<xsum<x的情況是我們要求出最小生成樹後,把每條邊染成同種顏色,然後再斷掉其中某條邊,連一條更大的邊。
先求出三個東東:設gsa,gsb,gscgsa,gsb,gsc分別表示dis<xsum,dis=xsum,dis>xsumdis<x-sum,dis=x-sum,dis>x-sum
那麼我們發現,gsagsa的邊必須都是和生成樹的邊的顏色相同,否則生成樹必選這些邊。
gsbgsb的邊就可以隨便搞,但是要注意不能全部的這些邊都染成和生成樹一樣的顏色。
gscgsc的邊就是無關邊,隨意搞。
答案即爲:2(2gsb1)(2gsc)2*(2^{gsb}-1)*(2^{gsc})

標程

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const long long mo=1000000007;
const int maxn=200010;

int n,m,x[maxn],y[maxn],f[maxn];
int fa[maxn],dep[maxn];
int tot,nex[maxn*2],las[maxn*2],tov[maxn*2];
bool bz[maxn];
long long ans,X,sum,v[maxn],z[maxn],val[maxn*2];

void insert(int x,int y,long long z)
{
	tot++;
	tov[tot]=y;
	nex[tot]=las[x];
	las[x]=tot;
	val[tot]=z;
}

void qsort(int l,int r)
{
	int i=l;int j=r;
	int m=z[(i+j)/2];
	while (i<=j)
	{
		while (z[i]<m) i++;
		while (z[j]>m) j--;
		if (i<=j)
		{
			swap(x[i],x[j]);
			swap(y[i],y[j]);
			swap(z[i],z[j]);
			i++;j--;
		}
	}
	if (l<j) qsort(l,j);
	if (r>i) qsort(i,r); 
}

int getfather(int x)
{
	if (f[x]==x) return x;
	f[x]=getfather(f[x]);
	return f[x];
}

long long qsm(long long a,long long b)
{
	long long t=1;
	long long y=a;
	while (b>0)
	{
		if ((b&1)==1) t=t*y%mo;
		y=y*y%mo;
		b/=2;
	}
	return t;
}

void dfs(int x,int ff)
{
	fa[x]=ff;
	dep[x]=dep[ff]+1;
	for (int i=las[x];i;i=nex[i])
	{
		if (tov[i]!=ff)
		{
			dfs(tov[i],x);
			v[tov[i]]=val[i];
		}
	}
}

int main()
{
	scanf("%d%d",&n,&m);
	scanf("%lld",&X);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%lld",&x[i],&y[i],&z[i]);
	}
	for (int i=1;i<=n;i++) f[i]=i;
	qsort(1,m);
	for (int i=1;i<=m;i++)
	{
		int xx=getfather(x[i]);
		int yy=getfather(y[i]);
		if (xx!=yy)
		{
			f[xx]=yy;
			bz[i]=true;
			sum+=z[i]; 
			insert(x[i],y[i],z[i]);
			insert(y[i],x[i],z[i]);
		}
	}
	dfs(1,0);
	
	for (int i=1;i<=m;i++)
	{
		if (!bz[i])
		{
			int xx=x[i];
			int yy=y[i];
			long long zd=0;
			while (xx!=yy)
			{
				if (dep[xx]>dep[yy])
				{
					zd=max(zd,v[xx]);
					xx=fa[xx];
				}
				else
				{
					zd=max(zd,v[yy]);
					yy=fa[yy];
				}
			}
			z[i]=z[i]-zd;
		}
	}
	
	if (sum>X)
	{
		printf("0\n");
	}
	else
	if (sum==X)
	{
		long long gs=0;
		for (int i=1;i<=m;i++)
		{
			if (!bz[i])
			{
				if (z[i]==0) gs++;
			}
		}
		ans=(qsm(2,gs+n-1)-2+mo)%mo;
		ans=ans*qsm(2,m-gs-n+1)%mo;
//		ans=(ans+(2*(qsm(2,gs)-1)%mo)%mo)%mo;
//		printf("%0\n");
		printf("%lld\n",ans);
	}
	else
	{
		long long op=X-sum;
		long long gsa=0;long long gsb=0;long long gsc=0;
		for (int i=1;i<=m;i++)
		{
			if (!bz[i])
			{
				if (z[i]<op)
				{
					gsa++;
				}
				else
				if (z[i]==op)
				{
					gsb++;
				}
				else 
				{
					gsc++;
				}
			}
		}
		if (gsb==0)
		{
			printf("0\n");
			return 0;
		}
		else
		{
			ans=2;
			ans=ans*((qsm(2,gsb)-1+mo)%mo)%mo;
			ans=ans*qsm(2,gsc)%mo;
			printf("%lld\n",ans);
		}
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章