zoukankan      html  css  js  c++  java
  • 【CodeForces】914 H. Ember and Storm's Tree Game 动态规划+排列组合

    【题目】H. Ember and Storm's Tree Game

    【题意】Zsnuoの博客

    【算法】动态规划+排列组合

    【题解】题目本身其实并不难,但是大量干扰因素让题目显得很神秘。

    参考:Zsnuoの博客

    一、首先Ember必胜(考虑n个点连成一条链),故合法的树一定满足先手必胜。当Storm选择的链满足单调或单峰时,每一条链对答案贡献两对(i,op)

    解释:单调时,考虑翻转最后一个数和从第二个数开始取负两种操作。单峰时,上凸考虑翻转顶峰和顶峰右侧的数,下凸考虑取负顶峰和顶峰右侧的数。

    这样之后,我们就完全排除了博弈的因素(胜负和操作选择)。

    二、问题转化为:令S表示满足 [ 包含n个点 ] 且 [ 每个点出度<=d ] 且 [ 所有树链编号单调或单峰 ] 的树,由(一)得最终答案ANS=2*n*(n-1)*|S|,即求|S|

    其中单峰的路径比较容易考虑,S一定存在一个形态,满足所有单峰路径(u,v)的峰都是lca(u,v)。

    证明:如果有一条路径峰不在LCA,那么lca(u,v)向上延伸一定是单调的,那选该峰作为根即可。

    接下来考虑单调的路径(u,v)(u,v互不为祖先),易得如果lca(u,v)≠root就无法满足条件,所以不同单调性的指向根的链只能在根相遇。

    也就是,每棵树都存在至少一个根root,满足所有以root为端点的路径都是单调的。接下来我们只须统计同一单调性的树的个数,在根处拼起来即可。

    三、设f[i][j]表示包含i个点,根节点出度为j且满足 [ 所有节点编号均大于其父亲节点编号 ] 的树的个数。

    同排列DP一样,我们关心的只有子树节点编号的大小关系,和具体是什么无关。所以我们每次都视为编号为1~i的节点来统计即可。

    规定父亲节点编号为1,为了不重复统计,每次枚举根节点编号为2的子树大小k来转移,即:

    $$f(i,j)=sum _{k=1}^{i-1}f(i-k,j-1)*inom{i-2}{k-1}*sum _{l=0}^{d-1}f(k,l)$$

    从未确定的i-2个点中选择一些来构成子树中的k-1个点,即C(i-2,k-1),然后再按大小关系当成2~k排列。

    令$sum(i)=sum _{j=0}^{d-1}f(i,j)$,就可以前缀和优化:

    $$f(i,j)=sum _{k=1}^{i-1}f(i-k,j-1)*inom{i-2}{k-1}*sum(k)$$

    预处理组合数,初始化f(1,0)=1。(这样就不用理会非素数模数了,不然又得搞CRT那套,太麻烦了……)

    四、在根节点处拼接。

    假设两个根节点都为root的不同单调性的树拼接(根节点重合),枚举第一单调性的点数i,第一单调性所占度数j,第二单调性所占度数k,即:

    $$|S|=sum _{i=1}^{n}sum _{j+kleq d}f(i,j)*f(n-i+1,k)$$

    最后一个问题,如果一棵树S存在多个root(例如n=2),那么这多个root一定构成一条单调链,其中一端是j=1且k≠1,另一端是j≠1且k=1,中间是j=1且k=1,我们只在第一种情况统计这棵树。

    那么,最终:

    $$|S|=sum _{i=1}^{n}sum _{j+kleq d,k eq 1}f(i,j)*f(n-i+1,k)$$

    复杂度O(n^3)。

    #include<cstdio>
    const int maxn=210;
    
    long long n,d,MOD,c[maxn][maxn],sum[maxn],ans,f[maxn][maxn];
    int main(){
        scanf("%lld%lld%lld",&n,&d,&MOD);
        for(int i=0;i<=n;i++){
            c[i][0]=1;
            for(int j=1;j<=i;j++)c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD;
        }
        f[1][0]=sum[1]=1;
        for(int i=2;i<=n;i++){
            for(int j=1;j<=d;j++){
                for(int k=1;k<i;k++){
                    f[i][j]=(f[i][j]+f[i-k][j-1]*c[i-2][k-1]%MOD*sum[k])%MOD;
                }
                if(j!=d)sum[i]=(sum[i]+f[i][j])%MOD;
            }
        }
        for(int i=1;i<=n;i++)
            for(int j=0;j<=d;j++)
                for(int k=0;j+k<=d;k++)if(k!=1)
                    ans=(ans+f[i][j]*f[n-i+1][k])%MOD;
        printf("%lld",2*n*(n-1)*ans%MOD);
        return 0;
    }
    View Code
  • 相关阅读:
    Python+selenium常见报错
    jmeter压力测试指标解释
    jemte自动生成测试报告方法
    iview清空Date-picker组件后请求报错解决方法
    【求助】Django+vue项目时候遇到一个问题,table的data数据怎么更新
    django_数据库操作—增、删、改、查
    django-关于manage.py migrate无效的问题
    python家里的环境和公司环境怎么同步
    python class类的属性应用及init初始化实践 ---勇者斗恶龙
    IDLE与pycharm执行相同代码结果却不同,原因分析
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8616117.html
Copyright © 2011-2022 走看看