zoukankan      html  css  js  c++  java
  • [ZJOI2010]排列计数

    注意观察题目:$Pi>Pi/2$。

    发现特别像什么?

    二叉堆

    于是就变成了:$n$个堆元素进行排列,满足堆性质的排列对$p$的取模。(堆性质根据题意为大根堆)

    设$f_i$为当前堆首为$i$的堆的排列方案数。为满足堆性质$P_i$显然只能取剩下若干数的最大值。

    发现$f_i$影响$f_{2i}$和$f_{2i+1}$两个结点。

    以$i$为根的堆去掉根节点的结点数为$n-i$个,提前统计出第$i$个结点左子树的大小$size_i$,则右边为$n-i-size_i$个结点。

    根据前面,可以得到这样的转移:$f_i=C(n-i, size) imes f_{2i} imes f_{2i+1}$。

    解释:从剩下的$n-i$个结点中选$size$个结点做其左子树,然后对于每个子树存在$f_{2i}$和$f_{2i+1}$中排列,根据乘法原理即得。

    正确性:这些数是一个排列,也就是说任意两个数一定不相等,可以构成一个严格递增的序列。挑出的若干数及挑出若干数后仍能构成严格递增的序列,所以可以化成子问题。

    边界:当前根结点为叶节点时,$f_i=1$。

    最后答案:$f_1$。

    注意:整个$DP$过程是$O(n)$的。但是存在计算组合数,复杂度为$O(n-i)$,$i$为当前根结点。根据递归主定理易知为$O(nlogn)$的复杂度。

    存在模数,就要提前处理逆元。对于$p$过小还要用$Lucas$定理,然而数据水而且我不会所以只写了逆元。

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 #define re register
     6 #define rep(i, a, b) for (re int i = a; i <= b; ++i)
     7 #define repd(i, a, b) for (re int i = a; i >= b; --i)
     8 #define maxx(a, b) a = max(a, b);
     9 #define minn(a, b) a = min(a, b);
    10 #define LL long long
    11 #define inf (1 << 30)
    12 
    13 inline int read() {
    14     int w = 0, f = 1; char c = getchar();
    15     while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar();
    16     while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar();
    17     return w * f;
    18 }
    19 
    20 const int maxn = 1e6 + 5;
    21 
    22 int n, p, f[maxn << 1], g[maxn << 1], inv[maxn];
    23 
    24 int C(int n, int m) {
    25     int res = 1;
    26     repd(i, n, n-m+1) res = (LL)res * i % p;
    27     rep(i, 1, m) res = (LL)res * inv[i] % p;
    28     return res;
    29 }
    30 
    31 int dfs(int u) {
    32     if (u > n) return 0;
    33     g[u] = dfs(u<<1);
    34     return g[u] + dfs(u<<1|1) + 1;
    35 }
    36 
    37 int dp(int u, int size) {
    38     if (f[u]) return f[u];
    39     if (u > n) return f[u] = 1;
    40     return f[u] = (LL)dp(u<<1, g[u]) * dp(u<<1|1, size-g[u]-1) % p * C(size-1, g[u]) % p;
    41 }
    42 
    43 int main() {
    44     n = read(), p = read();
    45     inv[1] = 1;
    46     g[1] = 1;
    47     rep(i, 2, n) {
    48         if (i < p)
    49             inv[i] = (LL)(p-p/i)*inv[p%i] % p;
    50         else
    51             inv[i] = inv[i % p];
    52     }
    53     dfs(1);
    54     printf("%d", dp(1, n));
    55     return 0;
    56 }
  • 相关阅读:
    CentOS7-Jenkins安装与配置
    jQuery-Ajax H5无刷新分页
    PHP使用MongoDB(CRUD)
    PHP使用Solr(CRUD)
    yum报错:One of the configured repositories failed (CentOS-7
    maevn的nexus私库搭建
    如何分析用户的行为:5个用户分类指标,3个用户分析的重点指标
    数据分析不落地?一个案例教会你!
    数仓建设全流程(附PPT和视频)
    干货 | 一文读懂数据分析
  • 原文地址:https://www.cnblogs.com/ac-evil/p/10367216.html
Copyright © 2011-2022 走看看