根本想不到ios
CF1097Ggit
給出一棵樹,定義f(S)爲用最少的邊連通點集$ S$的邊數spa
求$ \sum\limits f(S)^k$code
$ n \leq 10^5 k \leq 200$blog
假設$ k=1$有一個清真的樹形$ DP$get
在點集的$ LCA$處統計答案便可string
對於$ k>1$根據二項式定理it
能夠$ O(nk^2)$完成轉移io
但這是過不去的class
考慮
$$ 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; }