[CF995F]Cowmpany Cowmpensation[树形dp+拉格朗日插值]

题意

给你一棵树,你要用不超过 \(D\) 的权值给每个节点赋值,保证一个点的权值不小于其子节点,问有多少种合法的方案。

\(n\leq 3000, D\leq 10^9\)

分析

  • 如果 \(D\) 比较小的话可以考虑状态 \(f_{i,j}\) 表示点 \(i\) 的权值是 \(j\) 的方案总数,\(g_{i,j}\) 表示 \(\sum_\limits{k=1}^jf_{i,j}\) ,转移也比较显然:\(f_{i,j}=\prod g_{son,j}\)
  • 先证明结论:前?\(n\)?个正整数的?\(k\)?次幂之和是?\(k+1\)?次多项式(Imagine大佬果然牛逼)
  • 可以得到,若 \(f\) 是一个 \(k\) 次多项式,且 \(g(x)=\sum_\limits{i=0}^xf(i)\) ,那么 \(g\) 是一个 \(k+1\) 次多项式。 故一个点的 \(g\) 函数的次数是它的子树大小。
  • 算出 \(D\in [0,n]\) 时的值,然后插值一波即可。
  • 因为值连续插值部分可以优化到 \(O(n)\) ,总时间复杂度为 \(O(n^2)\)。

这份代码是5个月前的有点丑陋

代码


#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<cctype>
#include<queue>
#define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].last,v=e[i].to)
using namespace std;
typedef long long LL;
const LL mod=1e9 + 7;
const int N=3004;
int n,edcnt;
int head[N];
LL f[N][N],ans,D;
struct edge{
    int last,to;
    edge(){}edge(int last,int to):last(last),to(to){}
}e[N*2];
void Add(int a,int b){
    e[++edcnt]=edge(head[a],b);head[a]=edcnt;
    e[++edcnt]=edge(head[b],a);head[b]=edcnt;
}
void dfs(int u,int fa){
    for(int i=1;i<=n+1;i++) f[u][i]=1ll;
    go(u)if(v^fa){
      dfs(v,u);
      LL s=0ll;
      for(int j=1;j<=n+1;j++){
        (s+=f[v][j])%=mod;
        f[u][j]=f[u][j]*s%mod;
      }
    }
}
LL Pow(LL a,LL b){
  LL res=1ll;
  for(;b;b>>=1,a=a*a%mod) if(b&1) res=res*a%mod;
  return res;
}
int main(){
   scanf("%d%I64d",&n,&D);
   for(int i=2,x;i<=n;i++){
     scanf("%d",&x);
     Add(i,x);
   }
   dfs(1,0);
   for(int i=2;i<=n+1;i++) (f[1][i]+=f[1][i-1])%=mod;
   for(int i=1;i<=n+1;i++){
     LL fz=f[1][i],fm=1ll;
     for(int j=1;j<=n+1;j++)if(i^j){
        fz=(fz*(D-j)%mod+mod)%mod;
        fm=(fm*(i-j)%mod+mod)%mod;
     }
     (ans+=fz*Pow(fm,mod-2)%mod)%=mod;
   }
   printf("%I64d\n",ans);
   return 0;
}

原文地址:https://www.cnblogs.com/yqgAKIOI/p/10158781.html

时间: 2024-11-07 23:31:54

[CF995F]Cowmpany Cowmpensation[树形dp+拉格朗日插值]的相关文章

BZOJ4599[JLoi2016&amp;LNoi2016]成绩比较(dp+拉格朗日插值)

这个题我们首先可以dp,f[i][j]表示前i个科目恰好碾压了j个人的方案数,然后进行转移.我们先不考虑每个人的分数,先只关心和B的相对大小关系.我们设R[i]为第i科比B分数少的人数,则有f[i][j]=sum f[i-1][k]*C(k,j)*C(n-1-k,R[i]-j)  (k>=j) 怎么解释呢,首先前i-1科有k个人已经被碾压,k肯定大于等于j,然后考虑当前这一科有j个人被碾压,那么就需要从k个人中选出j个来即C(k,j),然后从剩下的有R[i]-j个人比B考的少,这些人必须是之前i

CF995F Cowmpany Cowmpensation

题目链接:http://codeforces.com/contest/995/problem/F 题目大意: 给定一棵 \(n\) 个节点的有根树(根为 \(1\) 号结点),为这棵树上的每一个结点赋值(赋值的范围为 \([1,D]\)),要求父结点的值不小于子结点值,问有多少种赋值方案. \(1 \le n \le 3000, 1 \le D \le 10^9\) 知识点: 树形DP.拉格朗日插值法 解题思路: 可以证明:对于一棵有 \(n\) 个结点的树,其答案是一个关于 \(D\) 的 \

bzoj 4559 [JLoi2016]成绩比较 —— DP+拉格朗日插值

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559 看了看拉格朗日插值:http://www.cnblogs.com/ECJTUACM-873284962/p/6833391.html https://blog.csdn.net/lvzelong2014/article/details/79159346 https://blog.csdn.net/qq_35649707/article/details/78018944 还只会最简单的

CF995F Cowmpany Cowmpensation 动态规划+容斥原理

数数题还是要多练啊 code: #include <cstdio> #include <cstring> #include <string> #include <vector> #include <algorithm> #define N 3004 #define ll long long #define mod 1000000007 #define setIO(s) freopen(s".in","r",

【BZOJ】4559: [JLoi2016]成绩比较 计数DP+排列组合+拉格朗日插值

[题意]n位同学(其中一位是B神),m门必修课,每门必修课的分数是[1,Ui].B神碾压了k位同学(所有课分数<=B神),且第x门课有rx-1位同学的分数高于B神,求满足条件的分数情况数.当有一位同学的一门必修课分数不同时视为两种情况不同.n,m<=100,Ui<=10^9. [算法]计数DP+排列组合+拉格朗日插值 [题解]把分数作为状态不现实,只能逐门课考虑. 设$f[i][j]$表示前i门课,有j个同学被碾压的情况数,则有: $$f[i][j]=g(i)\cdot\sum_{k=j

【BZOJ】2655: calc 动态规划+拉格朗日插值

[题意]一个序列$a_1,...,a_n$合法当且仅当它们都是[1,A]中的数字且互不相同,一个序列的价值定义为数字的乘积,求所有序列的价值和.n<=500,A<=10^9,n+1<A<mod<=10^9,mod是素数. [算法]动态规划+拉格朗日插值 [题解]这道题每个数字的贡献和序列选了的数字积关系密切,所以不能从序列角度考虑(和具体数字关系不大). 设$f_{n,m}$表示前n个数字(值域)中取m个数字的答案,那么枚举取或不取数字n,取n时乘n且有j个位置可以插入,即:

HDU-2196 Computer (树形DP)

最近在看树形DP,这题应该是树形DP的经典题了,写完以后还是有点感觉的.之后看了discuss可以用树分治来做,以后再试一试. 题目大意 找到带权树上离每个点的最远点.︿( ̄︶ ̄)︿ 题解: 对于每一个点的最远点,就是以这个点为根到所有叶子节点的最长距离.但是如果确定根的话,除了根节点外,只能找到每个节点(度数-1)个子树的最大值,剩下一个子树是该节点当前的父亲节点. 所以当前节点的最远点在当前节点子树的所有叶子节点以及父亲节点的最远点上(当父亲节点的最远点不在当前节点的子树上时), 如果父亲节

UVA-01220 Party at Hali-Bula (树形DP+map)

题目链接:https://vjudge.net/problem/UVA-1220 思路: 树形DP模板题,求最大人数很简单,难点在于如何判断最大人数的名单是否有不同的情况: 解决方法是用一个数组f[manx][2]记录该节点是否出场的情况,为真时代表有多种情况; 具体讨论: 当父节点的值加上某个子节点的值时,他的f的情况也和该子节点一样: 当某个节点dp(i, 0) == dp(i, 1), 则该节点以及它的父节点也一定有多种情况(父节点必定取其中之一). Code: 1 #include<bi

HDU 1520 树形dp裸题

1.HDU 1520  Anniversary party 2.总结:第一道树形dp,有点纠结 题意:公司聚会,员工与直接上司不能同时来,求最大权值和 #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b using nam