[CF995F]Cowmpany Cowmpensation

codeforces

description

一棵\(n\)个节点的树,给每个节点标一个\([1,m]\)之间的编号,要求儿子的权值不大于父亲权值。求方案数。\(n\le3000,n\le10^9\)

sol

可以证明答案是关于\(m\)的一个\(n\)次多项式。我不会证。
如果\(P(x)\)是关于\(x\)\(n\)次多项式,则有
\[P(x)=\sum_{i=0}^{n}(-1)^{n-i}P(i)\frac{x(x-1)...(x-n)}{(n-i)!i!(x-i)}\]
可见杜教\(\mbox{PPT}\)《多项式与求和》。
所以只要对\([1,n]\)求答案就可以了,很显然是一个\(O(n^2)\)\(dp\),所以复杂度是\(O(n^2)\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 3005;
const int mod = 1e9+7;
int n,m,nxt[N],head[N],f[N][N],inv[N],ans;
void dfs(int u){
    for (int i=1;i<=n;++i) f[u][i]=1;
    for (int v=head[u];v;v=nxt[v]){
        dfs(v);
        for (int i=1;i<=n;++i)
            f[u][i]=1ll*f[u][i]*f[v][i]%mod;
    }
    for (int i=2;i<=n;++i) (f[u][i]+=f[u][i-1])%=mod;
}
int main(){
    n=gi();m=gi();inv[0]=inv[1]=1;
    for (int i=2;i<=n;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    for (int i=2,ff;i<=n;++i)
        nxt[i]=head[ff=gi()],head[ff]=i;
    dfs(1);
    if (m<=n) return printf("%d\n",f[1][m]),0;
    for (int i=1;i<=n;++i){
        int sum=f[1][i];
        for (int j=0;j<=n;++j)
            if (j!=i) sum=1ll*sum*(m-j)%mod*(i>j?inv[i-j]:mod-inv[j-i])%mod;
        (ans+=sum)%=mod;
    }
    printf("%d\n",ans);return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/9387662.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值