校内胡测--8.26.2017 超级树

来源:互联网 发布:.com.cn域名个人备案 编辑:程序博客网 时间:2024/04/30 14:31

pdf上的没法复制粘贴…
无奈的手打qwq


题目:

这道题貌似是某次CF的比赛题,但是没找到原题=。=
一颗k-超级树可以按照如下方法得到:取一棵深度为K的满二叉树,对每个节点,向他的所有祖先连边(如果这条边不存在的话)。例如,下图是一个4-超级树
这里写图片描述

现在,我们的任务是统计一棵k-超级树中有多少条每个节点最多经过一次的不同有向路径。两条路径被认为是不同的,当且仅当两条路径途经点不同或者经过节点顺序不同。由于答案可能很大,输出方案数对mod去模后的数。

输入&输出

输入一行两个数字,分别为K 和 mod
输出一行为方案数取模mod后的数

样例:

in1:1 10000000
out1:1

in2:2 10000000
out2:9

in3:3 10000000
out3:245

in4:4 10000000
out4:126565


题解

放心吧这题,oeis是没有的hhhhh

说正事,这题,给的正解是dp。
dp[i][j]表示一棵i-超级树中选出j条不经过相同点路径的条数(这j条路径经过点的集合并集为空)
转移是这样的:
对于两颗i-超级树,他们合成为一棵(i+1)-超级树的时候,会出现一个新的树根,下面简称根。
令num = dp[i][lf] * dp[i][rg] ;
1.这个根什么也不干,直接合并

dp[i][lf+rg] += num//相当于是从左边选lf条边,右边选rg条边

2.这个根自己一个点作为一条新的路径

dp[i+1][lf+rg+1] += num

3.这个根他和左子树(或右子树)的任意一条边连接(随便连上哪一条是都是可以的,因为它是一棵超级树,根到所有节点都有边)

dp[i+1][lf+rg] += 2*num*(lf+rg);/*乘2是因为,我既可以接到首,又可以接到尾比如本来有一条是1->2->3现在可以是root->1->2->3或者是1->2->3->root*/

4.这个根连接了左右子树中各一条边

dp[i+1][lf+rg-1] += 2*num*lf*rg//乘2道理是一样的//乘lf*rg相当于是左右任选一种,乘法原理

5.这个根连接了左子树(或右子树)中两条边

dp[i+1][lf+rg-1] += 2*num*(C2[lf]+C2[rg])//其中,C2[i]表示的是C(i , 2 )

这样转移就完成了,最后答案就是DP[N][1]

但是我们会惊奇的发现,第二维根本开不下,如果强行要存下dp这个数组,第二维需要开(2^k)-1那么大(因为每个点都可以作为一条路径)。
但是仔细的观察DP方程,发现 ,由i转移到i+1时,第二维度只会-1,最多只会从(lf+rg)转移到(lf+rg-1),那么,对dp[N][1]有贡献的,一定都在dp[i][N]之内。于是我们第二维只需要开到N就可以了

好了写完了,贴代码。


这代码很慢

有很多地方还可以优化,可以动脑子想一想hhhh
这个代码刚好能卡时过,最后一个点1.000秒。

#include<ctime>#include<cstdio>#include<cstring>#include<algorithm>using namespace std ;long long N , mmod , C2[301];long long dp[301][301] ;int main(){    //freopen( "tree.in" , "r" , stdin ) ;    //freopen( "tree.out", "w" , stdout) ;    scanf ( "%I64d%I64d" , &N , &mmod ) ;    for( int i = 1 ; i <= 300 ; ++ i )        C2[i] = i*(i-1)/2 ;    dp[1][0] = dp[1][1] = 1 ;    for(int i = 1 ; i < N ; ++ i ){        for(int lf = 0 ; lf <= N ; ++ lf ){            for(int rg = 0 ; rg + lf <= N ; ++ rg ){                long long num = dp[i][lf] * dp[i][rg] %mmod ;                dp[i+1][lf+rg]   += num ; if( dp[i+1][lf+rg] >= mmod ) dp[i+1][lf+rg] -= mmod ;                dp[i+1][lf+rg+1] += num ; if( dp[i+1][lf+rg+1]>=mmod ) dp[i+1][lf+rg+1] -= mmod ;                dp[i+1][lf+rg]   = ( dp[i+1][lf+rg] + (num*(lf+rg)<<1) ) % mmod ;                dp[i+1][lf+rg-1] = ( dp[i+1][lf+rg-1] + (num*lf*rg<<1) ) % mmod ;                dp[i+1][lf+rg-1] = ( dp[i+1][lf+rg-1] + (num*(C2[lf]+C2[rg])<<1) ) % mmod ;            }        }    }    printf("%I64d" , dp[N][1] % mmod ) ;    return 0 ;}
原创粉丝点击