Codeforces 1097G
根本想不到
题意
给出一棵树,定义f(S)为用最少的边连通点集$ S$的边数
求$ \sum\limits f(S)^k$
$ n \leq 10^5 k \leq 200$
题解
假设$ k=1$有一个清真的树形$ DP$
在点集的$ LCA$处统计答案即可
对于$ k>1$根据二项式定理
可以$ O(nk^2)$完成转移
但这是过不去的
考虑
$$ x^k=\sum_{i=0}^k \binom{x}{i}S(k,i)i!$$
其中$ S(i,j)$表示第二类斯特林数
拆开组合数得
$$ x^k=\sum_{i=0}^k \frac{x!}{(x-i)!}S(k,i)$$
因此我们只要维护所有的下降幂就可以还原出$ x^k$
诶等等...这复杂度还是$ nk^2$的啊...
冷静分析一下,假设当前选取的边集大小不超过$ k$那下降幂为$ 0$
因此我们只需要枚举到$ min(当前非0下降幂的长度,k)$即可
根据树上背包的复杂度分析,其实是$ O(nk)$的
代码
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 1000000007 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt; vector<int>e[100010]; int S[205][205],f[100100][205],mi[100010],sz[100010],ans[100010]; void dfs(int x,int pre){ f[x][0]=sz[x]=1; for(auto v:e[x])if(v!=pre){ dfs(v,x); for(rt i=min(sz[v]-1,k-1);i>=0;i--){ int val=f[v][i]; if(!i)val=1ll*val*(1-mi[sz[v]])%p; (ans[i+1]+=1ll*val*(1-mi[n-sz[v]])%p)%=p; (f[v][i+1]+=val)%=p; } for(rt i=min(sz[x]-1,k);i>=0;i--) for(rt j=1;j<=sz[v]&&i+j<=k;j++){ const int val=1ll*f[x][i]*f[v][j]%p; if(i)(ans[i+j]+=val)%=p; (f[x][i+j]+=val)%=p; } sz[x]+=sz[v]; } } #define inv2 500000004 int main(){ n=read(),k=read(); S[0][0]=1; for(rt i=1;i<=k;i++) for(rt j=1;j<=i;j++)S[i][j]=(S[i-1][j-1]+1ll*S[i-1][j]*j%p)%p; for(rt i=1;i<n;i++){ x=read();y=read(); e[x].push_back(y); e[y].push_back(x); } mi[0]=1; for(rt i=1;i<=n;i++)mi[i]=1ll*mi[i-1]*inv2%p; dfs(1,0); int ret=0,jc=1; for(rt i=0;i<=k;i++)(ret+=1ll*S[k][i]*jc%p*ans[i]%p)%=p,jc=1ll*jc*(i+1)%p; for(rt i=1;i<=n;i++)ret=2ll*ret%p; cout<<(ret+p)%p; return 0; }