zoukankan      html  css  js  c++  java
  • 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
    ", ans);
    70 }
    71 
    72 int main() {
    73     init();
    74     solve();
    75     return 0;
    76 }
  • 相关阅读:
    命令模式
    软件设计师_例题
    软件设计师_计算机系统基础(1.1)
    ForkJoin之ForkJoinTask框架学习笔记
    Oracle使用语句块之循环插入数据
    SpringCloud搭建分布式配置中心(基于git)
    Docker配置JDK1.8
    Linux命令查看文件内容
    Mac配置maven环境命令
    Docker安装mysql8
  • 原文地址:https://www.cnblogs.com/yyf0309/p/9308869.html
Copyright © 2011-2022 走看看