CF981H K Paths

CF981H K Paths 

题解

一道不错的分治ntt题目

 

题目稍微转化一下,就是所有k条链的存在交,并且交的部分都被覆盖k次

所以一定是两个点,之间路径选择k次,然后端点两开花

 

f[x]表示x子树内往下延伸k条链(可以停在x)的方案数(有标号)

每个子树选择一个或者不选择,最多一共选择k个,dp是O(n^2)的,

考虑生成函数,其实就是:

 

统计答案?

直接两两点对f相乘肯定不行,因为f仅仅是子树

考虑枚举x作为lca统计

如果是拐弯的链,树形DP即可。

而如果是直上直下的链,

对于不同子树,x的选择是扣去这个子树,还可以往上选择

分治NTT的经典问题

const int N=1e5+5;
int n,k;
int jie[N],inv[N];
int A(int n,int m){
    if(n<0||m<0||n<m) return 0;
    return mul(jie[n],inv[n-m]);
}
int val[N],si[N];
void divi(int l,int r,Poly &f,Poly &g){
    // cout<<" divi "<<l<<" "<<r<<endl;
    if(l==r){
        g.resize(1);g[0]=val[l];
        f.resize(2);f[0]=1;f[1]=si[l];
        return;
    }
    Poly lf,lg,rf,rg;
    int mid=(l+r)>>1;
    divi(l,mid,lf,lg);
    divi(mid+1,r,rf,rg);
    f=lf*rf;
    g=(lf*rg)+(lg*rf);
}
int f[N],sum[N],son[N];
int ans,sz[N];
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
void dfs(int x,int fa){
    // cout<<" xx "<<x<<" fa "<<fa<<endl;
    sz[x]=1;
    int pre=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        ++son[x];
        dfs(y,x);
        sz[x]+=sz[y];
        sum[x]=ad(sum[x],sum[y]);
        ans=ad(ans,mul(pre,sum[y]));
        pre=ad(pre,sum[y]);
    }
    if(son[x]){
        Poly F,G;
        int ct=0;
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa) continue;
            val[++ct]=sum[y];
            si[ct]=sz[y];
        }
        divi(1,ct,F,G);
        Poly T;
        T.resize(2);
        T[0]=1;T[1]=n-sz[x];
        G=G*T;
        for(reg i=0;i<=min(k,son[x]);++i){
            // cout<<"F["<<i<<"] "<<F[i]<<" G["<<i<<"] "<<G[i]<<endl;
            ans=ad(ans,mul(A(k,i),G[i]));
            f[x]=ad(f[x],mul(A(k,i),F[i]));
        }
        sum[x]=ad(sum[x],f[x]);
    }else{
        f[x]=1;sum[x]=1;
    }
    // cout<<" return "<<x<<" f "<<f[x]<<endl;
}
int main(){
    rd(n);rd(k);
    if(k==1){
        ans=(ll)n*(n-1)/2%mod;
        ot(ans);return 0;
    }
    jie[0]=1;
    for(reg i=1;i<=k;++i) jie[i]=mul(jie[i-1],i);
    inv[k]=qm(jie[k],mod-2);
    for(reg i=k-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);

    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);add(x,y);add(y,x);
    }
    dfs(1,0);
    ot(ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/4/8 18:57:00
*/

 

posted @ 2019-06-07 16:37  *Miracle*  阅读(510)  评论(0编辑  收藏  举报