• Codeforces 1097G


    根本想不到

    CF1097G


    题意

    给出一棵树,定义f(S)为用最少的边连通点集$ S$的边数

    求$ sumlimits 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 inom{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('
    ');}
    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;
    }
  • 相关阅读:
    面试模板|如何给面试官做自我介绍?
    端口被占用怎么办
    谈C#中编码Encoding
    使用SQLServer复制数据库
    服务器主要参数
    Java学习笔记封装Java Util包Base64方法
    线上问题的一次锁思考
    windows服务器远程桌面远程粘贴无效
    kubeadm部署报错
    获取kubeadmin部署时所需的images镜像脚本
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10245945.html
Copyright © 2020-2023  润新知