• 【洛谷6672】[清华集训2016] 你的生命已如风中残烛(Raney引理)


    点此看题面

    • 共有(m)张牌,其中(n)张牌上写有数字(w_i),满足(sum w_i=m)
    • 一开始你先摸走牌库最顶上的一张牌,每当你摸到一张写着数字(x)的牌,你就再摸(x)张牌。
    • 问有多少种牌库,你能把所有牌摸完。
    • (nle40,w_ile10^5)

    (Raney)引理

    做这道题必需的一个引理:

    一个环(x_1,x_2,...,x_n)满足(sum x_i=1),在将它裂开形成的(n)条可能的链中,恰有一条满足所有前缀和都是正数。

    懒得另开一篇博客,就在这里顺便讲一下用(Raney)引理证明卡特兰数(C_n=frac{(2n)!}{n!(n+1)!})

    因为共有(n)(1)(n)(-1),它们的和不为(1),因此我们强行在序列开头加上一个(1),这样就满足所有数和为(1)

    由于所有前缀和为正数,因此第一个数肯定是(1)而不是(-1),那么去掉开头就变成了剩余所有前缀和非负,恰好满足卡特兰数的限制条件。

    这样就能推导出卡特兰数的通项公式(C_n=frac{(2n)!}{n!(n+1)!})

    关于此题的解法

    首先我们把所有数都减去(1),此时所有数和为(0),且需要满足所有前缀都是非负数。

    一个(naive)的想法是和前面卡特兰数的推导一样,强行在序列开头加上一个(1),满足所有数和为(1),且变成所有前缀都是正数。

    但是,毕竟还是不一样,因为卡特兰数中正数只有(1),开头必须是(1),而这道题则不一定,所以会计算出错。

    于是我们必须换个思路,发现有一个东西是唯一的,就是负数只有(-1)

    所以我们改成在序列末尾加上一个(-1),那么序列总和是(-1),这样一来只要将所有数都取反并逆序,就转化成了(Raney)引理的标准模型,且可以保证计算不重复(因为取反后的正数就只有(1)了)。

    此时共有(m+1)个数,因此圆排列数量应该是((m+1-1)!=m!)

    然后为了除掉这个(-1)的影响,考虑原本有(m-n)(-1),现在添上一个(-1)就有(m-n+1)(-1),为了消除贡献还需要将(m!)除去(m-n+1)

    因此答案就是:

    [frac{m!}{m-n+1} ]

    因为(m-n+1le m),所以只要在求阶乘的时候忽略其影响即可。

    代码:(O(sum w_i))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 40
    #define X 998244353
    using namespace std;
    int n,m;
    int main()
    {
    	RI i,t;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&t),m+=t;//m为所有数的总和
    	for(t=i=1;i<=m;++i) i^(m-n+1)&&(t=1LL*t*i%X);return printf("%d
    ",t),0;//计算答案,忽略m-n+1
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    springboot(十)使用LogBack作为日志组件
    springboot(九)文件上传
    django 安装
    macbook使用“终端”远程登录linux主机
    Mac 怎么通过自带终端连接linux服务器
    什么是变量?
    选择最好用的PyCharm IDE
    开发你的第一个Python程序
    Python介绍
    PyCharm 2017 安装教程
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6672.html
Copyright © 2020-2023  润新知