正好想打场AT,然后模拟赛又出了这场的 F 题,于是就做了……
E - Bichrome Spanning Tree
给定一个 (n) 点 (m) 边无向图,对边黑白染色,使得满足包含两种颜色的最小生成树的权值和为 (X) .
首先求出原图的最小生成树,设权值和为 (ans_0) ,如果 (ans_0>X) 显然无解。
否则,依次枚举每条边并钦定选择,计算出这种情况下的最小生成树权值。记其中 (=X) 的个数为 (cnt_1) , (>X) 的个数为 (cnt_2) 。
如果 (ans_0<X) ,答案是 (2(2^{cnt_1}-1) imes2^{cnt_2}) :(2) 是原 MST 的染色方式;(2^{cnt_1}-1) 是 (s=X) 的强制选择情况中的染色方式,因为至少有一条边要和原 MST 不同;(2^{cnt_2}) 是 (s>X) 的染色方式,任意染色。
如果 (ans_0=X) ,那么答案就是 ((2^{cnt_1}-2) imes2^{cnt_2}) 。
//Author: RingweEH
ll Kruskal( int chos )
{
ll res=0; int fx,fy;
for ( int i=0; i<=n+1; i++ ) fa[i]=i;
if ( chos ) { fx=find(e[chos].fro),fy=find(e[chos].to); fa[fx]=fy; res+=e[chos].val; }
for ( int i=1; i<=m; i++ )
{
if ( i==chos ) continue;
fx=find(e[i].fro); fy=find(e[i].to);
if ( fx^fy ) fa[fx]=fy,res+=e[i].val;
}
return res;
}
int main()
{
n=read(); m=read(); X=read();
for ( int i=1; i<=m; i++ ) e[i].fro=read(),e[i].to=read(),e[i].val=read();
sort(e+1,e+1+m); ll ans0=Kruskal(0);
if ( ans0>X ) { puts("0"); return 0; }
int cnt1=0,cnt2=0;
for ( int i=1; i<=m; i++ )
{
ll s=Kruskal(i);
if ( s==X ) cnt1++;
else if ( s>X ) cnt2++;
}
if ( ans0==X ) printf("%lld
",(power(2,cnt1)+Mod-2)%Mod*power(2,cnt2)%Mod );
else printf("%lld
",(2ll*power(2ll,cnt1)+Mod-2)%Mod*power(2,cnt2)%Mod );
return 0;
}
F - Dark Horse
(2^N) 个人,二叉树形式进行淘汰赛,一开始的顺序是 (2^N) 的任意一个排列。有 (M) 个人 (A_1sim A_M) 打得过 (1) ,(1) 能打得过其他所有人,如果对决中没有 (1) 那么编号小的获胜。求所有情况中 (1) 最终胜利的数量。
(1) 要赢,必须打败来自大小为 (2^0sim 2^{n-1}) 的子树的 (n) 个对手,走到二叉树顶端。
问题转化为:将 (2sim 2^n) 划分成 (n) 个集合,第 (i) 个大小为 (2^{i-1}) ,每个集合中的 (min) 不是 (M) 个人中的一个。
考虑容斥,对于一个状态 (S) ,第 (i-1) 位为 (1) 表示第 (i) 个集合的 (min) 是 (M) 个人之一。DP 预处理即可。注意这里是钦定 (1) 在第一个位置了,而 (1) 在其他位置等价,所以要乘上 (2^n) .
组合数没判 (n<m) 于是就喜提 RE
……
//Author: RingweEH
int power(int a,int b) //quick power
void Init( int n ) //Init fac,infac
int binom( int n,int m ) { return (n<m) ? 0 : 1ll*fac[n]*infac[m]%Mod*infac[n-m]%Mod; }
void bmod( int &x,int y ) { x+=y-Mod; x+=(x<0)*Mod; }
signed main()
{
n=read(); m=read(); int lim=(1<<n);
for ( int i=1; i<=m; i++ ) a[i]=read();
reverse(a+1,a+1+m); Init(lim);
dp[0][0]=1;
for ( int i=1; i<=m; i++ )
for ( int S=0; S<lim; S++ )
{
bmod(dp[i][S],dp[i-1][S]);
for ( int k=0; k<n; k++ )
if ( !(S>>k&1) )
bmod(dp[i][S|(1<<k)],1ll*dp[i-1][S]*binom(lim-a[i]-S,(1<<k)-1)%Mod);
}
ans=0;
for ( int i=0; i<lim; i++ )
{
int S=lim-1-i;
for ( int j=0; j<n; j++ )
if ( !(i>>j&1) ) dp[m][i]=1ll*dp[m][i]*binom(S,1<<j)%Mod,S-=1<<j;
if ( __builtin_popcount(i)&1 ) bmod(ans,Mod-dp[m][i]);
else bmod(ans,dp[m][i]);
}
ans=1ll*lim*ans%Mod;
for ( int i=0; i<n; i++ ) ans=1ll*ans*fac[1<<i]%Mod;
printf("%d
",ans );
return 0;
}