zoukankan      html  css  js  c++  java
  • 【codeforces 914H】Ember and Storm's Tree Game

    原题链接

    Description

    Ember和Storm正在玩游戏。首先,Ember构造一棵n个节点且每个节点度数不超过d的带节点编号的树T。然后,Storm选择两个不同的节点u和v,并写下从u到v路径上的节点编号,记为序列 a1, a2... a。最后,Ember在序列中选择一个位置 i(1 ≤ i < k),并在以下两个操作选择一个执行:

    • 翻转 ai+1... a并将这一段加上ai,操作后序列变为 a1, ... ai, ak + ai, ak-1 + ai, ... ai+1 + ai
    • 取负 ai+1... a并将这一段加上ai,操作后序列变为 a1, ... ai,  - ai+1 + ai,  - ai+2 + ai, ... - ak + ai

    如果最后的序列是严格单调的,则Ember获胜,否则Storm获胜。

    游戏情形可以用一个元组 (T, u, v, i, op) 来描述,op为翻转或是取负取决于Ember的决策。若Ember和Storm都使用最优策略(若有多种必胜策略,任选一种执行;若必败,也任选一种执行),试统计所有可能的游戏情形的数量,并输出其取模m的结果。

    Input

    仅一行,给出 n,d,m。 (2 ≤ n ≤ 200, 1 ≤ d < n, 1 ≤ m ≤ 2·109).

    Output

    输出一个数字——所有可能的游戏情形的数量取模m之后的结果。

     

    首先,Ember一定会构造出一棵能让自己必胜的树。而Ember获胜当而仅当原序列$a$为单调的或是单峰的;且对于每一个合法的序列,有2种合法的$(i,op)$的组合。没有什么好证明的……在草稿纸上自己模拟一下两种操作就可以得到了。

    问题转换为:统计满足以下条件的树的数量$S$:1. 包含$n$个节点,2. 每个节点度数不超过$d$,3. 树上任意两个节点间路径的编号序列为单调的或单峰的。最终答案为 $2cdot ncdot(n-1)cdot S$

    而对于一棵合法的树,一定存在一个特殊点,满足以这个节点为起点或终点的所有路径都是单调的。为了方便统计,我们令合法树的根节点为特殊点。观察可得,对于一棵合法树,除根节点以外的子树都满足:父亲节点编号大于儿子编号,或是父亲编号小于儿子编号。所以我们只需要统计这两种情况的答案,然后在根节点处拼起来即可。而实际上,这两种情况是等价的。

    $f(i,j)$表示节点数为$i$,根节点度数为$j$,且父亲编号小于儿子编号的方案数。

    枚举当前要拼接的子树大小$k$,钦定根节点编号最小,拼接过来的子树的根节点编号次小,可得到以下递推公式:

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

     $sum(i)=sum _{j=1}^{d-1}f(i,j)$,可得:

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

    时间复杂度为 $O(n^{3})$ ,初始化 $f(1,0)=sum(1)=1$

    (这种方法是在评论区看到的……然后参考了一下wxh大爷的博客。官方题解给了另一种统计f数组的方式,要稍微复杂一些,以及因为不保证m是质数,会有一些细节需要处理。详见官方题解,细节处理详见评论区。)

    统计出$f$数组后就可以开始拼接了,枚举满足父亲节点编号小于儿子编号的点数$i$、度数$j$, 满足父亲节点编号大于儿子编号的度数$k$,可得到以下公式:

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

    而实际上一棵合法树是可以有多个合法根的,比如最简单的$n=2$的情况,合法根既可以是$1$也可以是$2$。我们可以得出另一个结论,如果一棵树有多个合法根,那么这些点一定构成一条单调链,一端是$j=1$$k≠1$,另一端是$j≠1$$k=1$,中间是$j=1$$k=1$,我们把这棵树放在第一种情况统计。

    得到最终公式:

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

    代码如下:

     1 #include<cstdio>
     2 #include<algorithm> 
     3 #include<cstring>
     4 #define LL long long
     5 using namespace std;
     6 const int N=205;
     7 int n,d,mod;
     8 LL ans,sum[N],c[N][N],f[N][N];
     9 int main()
    10 {
    11     scanf("%d%d%d",&n,&d,&mod);
    12     for(int i=0;i<=n;i++)c[i][0]=1;
    13     for(int i=1;i<=n;i++)
    14         for(int j=1;j<=i;j++)
    15             c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
    16     sum[1]=1;f[1][0]=1;
    17     for(int i=2;i<=n;i++)
    18     {
    19         for(int j=1;j<=d;j++)
    20             for(int k=1;k<i;k++)
    21                 f[i][j]=(f[i][j]+f[i-k][j-1]*sum[k]%mod*c[i-2][k-1]%mod)%mod;
    22         for(int j=1;j<=d-1;j++)
    23             sum[i]=(sum[i]+f[i][j])%mod;
    24     }
    25     for(int i=0;i<=n-1;i++)
    26         for(int j=0;j<=d;j++)
    27             for(int k=0;j+k<=d;k++)
    28                 if(k!=1)ans=(ans+f[i+1][j]*f[n-i][k]%mod)%mod;
    29     printf("%lld",2*n*(n-1)*ans%mod);
    30     return 0;
    31 }
    View Code
  • 相关阅读:
    Alpha版(内部测试版)发布
    冲刺2-3
    冲刺2-2
    冲刺2-1
    团队绩效评价
    改进方案
    意见汇总
    27组评价
    冲刺10
    SOA
  • 原文地址:https://www.cnblogs.com/zsnuo/p/8608622.html
Copyright © 2011-2022 走看看