Codeforces 1097G

根本想不到

CF1097G


题意

给出一棵树,定义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;
}

 

posted @ 2019-01-09 17:52  Kananix  阅读(386)  评论(1编辑  收藏  举报

Contact with me