Codeforces 995F Cowmpany Cowmpensation - 组合数学

题目传送门

  传送点I

  传送点II

  传送点III

题目大意

  给定一个棵$n$个点的有根树和整数$D$,给这$n$个点标号,要求每个节点的标号是正整数,且不超过父节点的标号,根节点的标号不得超过D。

  很容易地能得到$O(nD)$的动态规划:设$f[i][j]$表示$i$号点标为$j$在它的子树内的方案数。

  写写它的转移方程:$f[i][j] = \prod_{s \in Son(i)}\sum_{k = 1}^{j} f[s][k]$。

  设$g[i][j]=\sum_{k = 1}^{j}f[i][k]$,那么转移方程就能写成:$f[i][j] = \prod_{s \in Son(i)} g[s][j]$。

  感觉很优美,考虑它有怎样的性质。

引理1 $f[i][j]$可以看成是关于$j$的多项式,其次数是$i$的子树大小减1,$g[i][j]$可以看成是关于$j$的多项式,其次数是$i$的子树大小。

  证明 考虑用归纳法。

  考虑叶节点,它的$f[i][j] = 1, g[i][j] = j$,因此对于叶节点成立。

  然后前一部分可以证明了。对于后一部分,因为$g[i]$是$f[i]$的前缀和函数,因此次数恰好比它大1(用差分)。

  然后后面的做法就很傻逼了。记录每个点$f, g$函数的在$1,\cdots,n + 1$处的取值,暴力计算。最后用逐差法插值,求$f[1][D]$。

  表示切完后才发现是Div 1。要不看标题,真以为是Div 2.

Code

 1 /**
 2  * Codeforces
 3  * Problem#995F
 4  * Accepted
 5  * Time: 140ms
 6  * Memory: 35400k
 7  */
 8 #include <iostream>
 9 #include <cstdlib>
10 #include <cstdio>
11 using namespace std;
12 typedef bool boolean;
13
14 const int N = 3005, M = 1e9 + 7;
15
16 void exgcd(int a, int b, int& x, int& y) {
17     if (!b)
18         x = 1, y = 0;
19     else {
20         exgcd(b, a % b, y, x);
21         y -= (a / b) * x;
22     }
23 }
24
25 int inv(int a, int n) {
26     int x, y;
27     exgcd(a, n, x, y);
28     return (x < 0) ? (x + n) : (x);
29 }
30
31 int n, D;
32 int fa[N];
33 int f[N][N];
34
35 int add(int a, int b) {
36     a = a + b;
37     if (a >= M)
38         a -= M;
39     if (a < 0)
40         a += M;
41     return a;
42 }
43
44 inline void init() {
45     scanf("%d%d", &n, &D);
46     for (int i = 2; i <= n; i++)
47         scanf("%d", fa + i);
48 }
49
50 inline void solve() {
51     for (int i = 1; i <= n; i++)
52         for (int j = 1; j <= n + 1; j++)
53             f[i][j] = 1;
54     for (int i = n; i; i--) {
55         for (int j = 2; j <= n + 1; j++)
56             f[i][j] = add(f[i][j], f[i][j - 1]);
57         if (i > 1)
58             for (int j = 1; j <= n + 1; j++)
59                 f[fa[i]][j] = f[fa[i]][j] * 1ll * f[i][j] % M;
60     }
61     for (int i = 2; i <= n + 1; i++)
62         for (int j = 1; j <= n - i + 2; j++)
63             f[i][j] = add(f[i - 1][j + 1], -f[i - 1][j]);
64     int ans = 0, C = 1;
65     for (int i = 1; i <= n + 1 && i <= D; i++) {
66         ans = add(ans, C * 1ll * f[i][1] % M);
67         C = C * 1ll * (D - i) % M * inv(i, M) % M;
68     }
69     printf("%d\n", ans);
70 }
71
72 int main() {
73     init();
74     solve();
75     return 0;
76 }

原文地址:https://www.cnblogs.com/yyf0309/p/9308869.html

时间: 2024-10-08 13:29:23

Codeforces 995F Cowmpany Cowmpensation - 组合数学的相关文章

Codeforces 223APartial Sums 数论+组合数学

题意很简单,求不是那么好求的,k很大 要操作很多次,所以不可能直接来的,印象中解决操作比较多无非线段树 循环节 矩阵 组合数等等吧,这道题目 也就只能多画画什么 的了 就以第一个案例为主吧 , 3 1 2 3 k我们依据画的次数来自己定好了 下面的每个数表示这个位置的 数由最初的 数组num[]中多少个数加起来得到的 当k为0的时候呢,就是 1 1 1 k为1的时候呢 1 2 3 k为2的时候呢 1 3 6 那么k为3的时候 1 4 10 这里看一下 从数组下标0开始,那么其实就是 C(i +

Codeforces 717A Festival Organization(组合数学:斯特林数+Fibonacci数列+推公式)

Codeforces 717A Festival Organization(组合数学:斯特林数+Fibonacci数列+推公式) 牛逼题.....推公式非常的爽...虽然我是看了别人的博客才推出来的... 0.1 斯特林数 下面要用到的是带符号的第一类斯特林数. \(x^{n\downarrow}=\prod_{i=0}^{n-1}(x-i)=\sum_{k=0}^ns(n,k)x^k\) 有递推公式\(s(n,m)=s(n-1,m-1)-(n-1)*s(n-1,m)\) 0.2 斐波那契数列的

Codeforces 37D Lesson Timetable - 组合数学 - 动态规划

题目传送门 神奇的门I 神奇的门II 题目大意 有$n$组学生要上课2次课,有$m$个教室,编号为$1$到$m$.要确定有多少种不同的安排上课的教室的方案(每组学生都是本质不同的),使得它们满足: 每组学生第一次上课的教室的编号小于等于第二次上课的教室的编号. 第$i$间教室在第一次上课时,恰好有$x_{i}$组学生在场. 第$i$间教室在某次上课时,中间包含的学生组数不能超过$y_{i}$. 输出答案模$10^{9} + 7$. 因为第一次上课恰好有多少人,所以这个方案数是可以直接用组合数,暂

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\) 的 \

Codeforces 396A 数论,组合数学

题意:给一个a数组,求b 数组的方案数,但是要求两者乘积相同. 分析: 不可能将它们乘起来,对于每个数质因数分解,得到每个质因子个数,遍历这些质因子,将某个质因子放到 对应的盒子里面,可以不放,方案数就是一个组合数,用插板法. 这里的素数板子挺好的,一方面可以用来判断,一方面存起来. 组合数,可以考虑用乘法逆元. 每个质因子个数hash一下. #include <bits/stdc++.h> using namespace std; const int MOD = 1e9 + 7; #defi

[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\)?个正

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",

codeforces 396A A. On Number of Decompositions into Multipliers(组合数学+数论)

题目链接: codeforces 396A 题目大意: 给出n个数的序列,求取和这个序列的积相同但是序列本身不同的个数. 题目分析: 组合数学的问题,对于每一个数我们可以将它分解质因数,然后统计整个序列的各个质因数的个数. 那么符合要求的序列一定用这些质因数(每个质因数的个数保持不变)组成的,所以我们可以利用组合数学中的插板法,对每个质因数进行划分,划分给n个数(存在一些数没有分到的情况),那么就是Cn?1质因数个数+n?1. 根据乘法原则,总的方案数就是每个质因数的划分数之积. AC代码: #

Codeforces 451D Count Good Substrings(组合数学)

题目链接:Codeforces 451D Count Good Substrings 题目大意:定义good string,就是就一个字符串的连续相同字符用一个该字符替代后,形成回文串的字符串.现在给出一个字符串,问说该字符串的子串中,为good string的串有多少个,分长度为奇数和偶数的输出. 解题思路:因为字符串的组成为a和b,所以只要是头尾相同的子串都是满足的.所以我们计算在奇数和偶数位置的奇数个数和偶数个数即可,然后用组合数学求出答案. #include <cstdio> #inc