AtCoder Regular Contest 093 E - Bichrome Spanning Tree

在这里插入图片描述

题意

给你一个图,每条边有一个边权。
现在你可以对每条边染色,染成黑色或白色。
一种合法的染色方案指的是染色完毕后,对原图求MST(最小生成树)。最小生成树满足所有树上的边至少包含一条黑色变和白色边。然后最小生成树的边权和要等于给出的X值。
求合法染色方案数。

思考历程

开始没什么想法。
然后我假设sum为求一遍MST的值。
发现我们可以对x分成三种情况,一种是 s u m > x sum>x sum>x,显然没有情况。
一种是 s u m = x sum=x sum=x,这种情况推了老半天,推出来的似乎还错了。
另外一种是 s u m < x sum<x sum<x,这个还是有个瓶颈。大致求出那些边放进去后sum可以变成x。
然鹅其他影响的边我就想不到了。
好蔡啊。

题解

看完题解就懂了。
其实再求一个 d i s ( i ) dis(i) dis(i)表示当前边放入生成树后对答案的影响即可。
然后我就发现我全会了。
d i s dis dis值其实可以 O ( n   l o g   n ) O(n\ log\ n) O(n log n)求,打个LCA即可。然鹅数据很水,暴力也可以。

s u m = x sum=x sum=x的情况就是求出MST后,设 g s gs gs表示 d i s dis dis值为0的边的个数。
然后答案即为: ( 2 n − 1 + g s − 2 ) ∗ ( 2 m − n − g s + 1 ) (2^{n-1+gs}-2)*(2^{m-n-gs+1}) (2n1+gs2)(2mngs+1)
左边:首先这些边可以随便染色,因为无论怎么组合它们组成的MST都是等于X的,减2表示这些边不能全部染成黑色或白色。
右边:无关的边就随便染色了。

s u m < x sum<x sum<x的情况是我们要求出最小生成树后,把每条边染成同种颜色,然后再断掉其中某条边,连一条更大的边。
先求出三个东东:设 g s a , g s b , g s c gsa,gsb,gsc gsa,gsb,gsc分别表示 d i s < x − s u m , d i s = x − s u m , d i s > x − s u m dis<x-sum,dis=x-sum,dis>x-sum dis<xsum,dis=xsum,dis>xsum
那么我们发现, g s a gsa gsa的边必须都是和生成树的边的颜色相同,否则生成树必选这些边。
g s b gsb gsb的边就可以随便搞,但是要注意不能全部的这些边都染成和生成树一样的颜色。
g s c gsc gsc的边就是无关边,随意搞。
答案即为: 2 ∗ ( 2 g s b − 1 ) ∗ ( 2 g s c ) 2*(2^{gsb}-1)*(2^{gsc}) 2(2gsb1)(2gsc)

标程

#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);
		}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值