Codeforces 1097G - Vladislav and a Great Legend(第二类斯特林数+树上背包)

Codeforces 题目传送门 & 洛谷题目传送门

首先看到这题我的第一反应是:这题跟这题长得好像,不管三七二十一先把 \(k\) 次方展开成斯特林数的形式,\(f(X)^k=\sum\limits_{i=0}^k\begin{Bmatrix}k\\i\end{Bmatrix}\dbinom{f(X)}{i}\),于是我们只需对所有 \(i\in[0,k]\) 求出 \(\sum\dbinom{f(X)}{i}\) 即可。

然后就不会做了/dk/wq

考虑 \(\dbinom{f(X)}{i}\) 的组合意义,相当于是从虚树的边中选择 \(i\) 条边染上颜色的方案数。故考虑 \(dp\),\(dp_{u,i}\) 表示对于 \(f(S)\subset subtree(u)\cup e(u,fa_u)\) 的点集 \(S\),从 \(f(S)\) 中选择 \(i\) 条边染上颜色的方案数,其中 \(e(u,v)\) 为 \(u,v\) 之间的边。

不难发现这东西转移实际上就是一个树上背包,\(dp_{u,i}=\sum\limits_{j=0}^idp_{v,j}dp_{u,i-j}\),初始值 \(dp_{u,0}=2\)(\(u\in S\) 或 \(u\notin S\))。最后,由于我们只考虑了 \(u\) 子树内的边的贡献,未考虑 \(u\to fa_u\) 的边的贡献,故需再令 \(dp_{u,i}=dp_{u,i}+dp_{u,i-1}\)(选择这条边或不选这条边)。

最后是计算答案,记 \(ans_i=\sum\dbinom{f(X)}{i}\),对于每个点 \(u\),考虑该点为虚树根时的贡献 ,就是 \(dp_{u,i}\) 减去只包含在一个子树内的情况,这个在计算 \(dp_{u,i}\) 的过程中算一下就行了。

根据背包均摊可知复杂度 \(\mathcal O(nk)\)。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=1e5;
const int MAXK=200;
const int MOD=1e9+7;
int n,k,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dp[MAXN+5][MAXK+5],siz[MAXN+5],tmp[MAXK+5],ans[MAXN+5];
int s[MAXK+5][MAXK+5],fac[MAXK+5];
void prework(int k){
	fac[0]=s[0][0]=1;for(int i=1;i<=k;i++) fac[i]=1ll*fac[i-1]*i%MOD;
	for(int i=1;i<=k;i++) for(int j=1;j<=i;j++) s[i][j]=(s[i-1][j-1]+1ll*j*s[i-1][j])%MOD;
}
void dfs(int x,int f){
	siz[x]=1;dp[x][0]=2;
	for(int e=hd[x];e;e=nxt[e]){
		int y=to[e];if(y==f) continue;
		dfs(y,x);memset(tmp,0,sizeof(tmp));
		for(int i=0;i<=min(siz[y],k);i++) for(int j=0;j<=min(k-i,siz[x]);j++)
			tmp[i+j]=(tmp[i+j]+1ll*dp[y][i]*dp[x][j])%MOD;
		siz[x]+=siz[y];for(int i=0;i<=min(siz[x],k);i++) dp[x][i]=tmp[i];
		for(int i=0;i<=min(siz[y],k);i++) ans[i]=(ans[i]-dp[y][i]+MOD)%MOD;
	}
	for(int i=0;i<=min(siz[x],k);i++) ans[i]=(ans[i]+dp[x][i])%MOD;
	for(int i=min(siz[x],k);i;i--) dp[x][i]=(dp[x][i]+dp[x][i-1])%MOD;
	dp[x][1]--;if(dp[x][1]<0) dp[x][1]+=MOD;
}
int main(){
	scanf("%d%d",&n,&k);prework(k);int sum=0;
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);dfs(1,0);
	for(int i=0;i<=k;i++) sum=(sum+1ll*fac[i]*s[k][i]%MOD*ans[i])%MOD;
	printf("%d\n",sum);
	return 0;
}
上一篇:CF1097G Vladislav and a Great Legend


下一篇:Dapr + .NET Core实战(九)本地调试