省选模拟题解

T1 题解

题意:有一张 n 个点的有标号无向图,分为了 k 个连通块,第 i 个连通块的大小是 si,每个连通块都是完全图(节点之间两两有边)。要加 k1 条边使得图连通,计算所有连边方案的权值和。假设第 i 个连通块被多加了 di 条边,那么该连边方案的权值为 i=1kdi!

sol:首先把原图看成缩点后的一棵树,假设现在 d 序列已经确定了对应的权值也就确定了。但是对应了多种树的形态。要求一个确定的 d 序列对应了多少种树的形态,转化为求 prufer 序列的个数即:

(k2)!i1k(di1)!

但是原图中第 i 个连通块连的边有 si 个点可以选择去连所以一个确定的 d 序列在原图中对应的连边方式为:

(k2)!i1k(di1)!×i=1ksidi

贡献为:

(k2)!i1k(di1)!×i=1ksidi×i=1kdi!

=(k2)!×i=1ksididi

然后(k2)! 是个定值,后面的考虑 DP ,我们设 fi,j 表示前 i 个连通块 l=1idl=ji=1ksididi 这个值。显然 f1,j=j×s1j 转移也很简单:

fi,j=t=1j1t×sit×fi1,jt

但是这个是O(k3) 的,考虑怎么优化,我们试着把fi,j 拆一下。

fi,j1=si×fi1,j2+2si2×fi1,j3+3si3×fi1,j4

fi,j=si×fi1,j1+2si2×fi1,j2+3si3×fi1,j3

fi,j+1=si×fi1,j+2si2×fi1,j1+3si3×fi1,j2

我们把 fi,j1 乘一个系数 si ,把 fi,j+1 乘上一个系数 1si 就会发现一件神奇的事情!

sifi,j1=0×fi1,j+0×fi1,j1+si2×fi1,j2+2si3×fi1,j3

fi,j=0×fi1,j+si×fi1,j1+2si2×fi1,j2+3si3×fi1,j3

1sifi,j+1=1×fi1,j+2si×fi1,j1+3si2×fi1,j2+4si3×fi1,j3

然后我们就可以抵消掉一大堆东西得到:

sifi,j1+1sifi,j+1=2fi,j+fi1,j

fi,j+1=2sifi,j+sifi1,jsi2fi,j1

换一下元

fi,j=2sifi,j1+sifi1,j1si2fi,j2

然后就可以 O(k2) 递推了。
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=7005,mod=998244353;
inline ll rd()
{
	ll f=1;char c;
	while(!isdigit(c=getchar())) if(c=='-') f=-1;
	ll x=c^48;
	while(isdigit(c=getchar())) x=(x<<3)+(x<<1)+(c^48);
	return f*x;
}
ll n,k,fac[N]={1},g[N],s[N];
int f[N][N<<1]; 
int main()
{
	n=rd(),k=rd();ll d=2*(k-1);
	for(int i=1;i<=k;i++) fac[i]=fac[i-1]*i%mod,s[i]=rd();g[0]=1;
	for(int i=1;i<=d;i++) g[i]=g[i-1]*s[1]%mod;
	for(int i=1;i<=d;i++) f[1][i]=g[i]*i%mod;
	for(int i=2;i<=k;i++)
		for(int j=i;j<=d;j++)
			f[i][j]=(2*s[i]*f[i][j-1]%mod-s[i]*s[i]%mod*f[i][j-2]%mod+s[i]*f[i-1][j-1]%mod+mod)%mod;
	cout<<(1ll*f[k][d]*fac[k-2]%mod+mod)%mod<<endl;
	return 0;
}
posted @   Re_Star  阅读(77)  评论(10编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示