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 ;
}