题意
给你一个图,每条边有一个边权。
现在你可以对每条边染色,染成黑色或白色。
一种合法的染色方案指的是染色完毕后,对原图求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})
(2n−1+gs−2)∗(2m−n−gs+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<x−sum,dis=x−sum,dis>x−sum
那么我们发现,
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∗(2gsb−1)∗(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);
}
}
}