校内胡测--8.26.2017 超级树

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 ;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Win32.pas API函数的简单调用,如建立进程,建立文件映射,建立、读取管道(可以捕捉DOS程序输出)等。 StrFuncs.pas 字符串处理单元,完全兼容宽字节处理(即使用wideString),特有的中文字符串处理函数(如简繁转换等等),经过多次优化,大多以编表的方式进行处理(一般来说是最快的处理方式)。 BiosHelp.pas  读取Bios信息的单元,兼容各种windows系统。 Streams.pas  流(TStream)输入输出处理单元,可以用来保存读取控件属性。 ShlFile.pas  各种文件操作,包括获得系统特殊路径,获取文件图标等。 RegExpr.pas  一个规则表达式类的单元。 ShareMemRep.pas  一个可以用来替代Delphi本身的内存管理的单元。 MessageDlg.pas 提供了一个高制定性的消息对话框。 Lists.pas  提供了很多个TList的扩展类,是学习很研究TList的好东西。 Calendar.pas  公历与农历换算和时间处理的函数单元,具体看里面的说明。 Clipboards.pas 提供一个剪贴板增强类,可支持保存和载入剪贴板,支持多重剪贴板。 ComputerInfo.pas 完整的系统信息检测单元,从软件到硬件,从CPU到鼠标,很全面。 AccessCtrls.pas 一个Access数据库操作单元。 FastIniFile.pas  可以用来替换DELPHI提供的慢吞吞的IniFiles单元,并且支持更多写入读出类型。 EnumStuff.pas 一个募举进程和窗口列表的单元,兼容各种Windows系统。 DES.pas  DES加密算法单元。 AES.pas  AES加密算法单元。 CryptoAPI.pas  一个完整的Hash算法单元,如MD5、CRC之类等等。 FastMM.pas  国外很著名的内存管理单元,Delphi2006的内存管理单元用的就是它。 FastStrings.pas  一个快速字符串处理单元,一些函数用汇编写的,处理速度比DELPHI本身的字符串处理快很多,不过不支持WideString类型。 Idpacker.pas  压缩文件类型检测单元。 ZLibEx.pas  纯Pascal代码的快速压缩解压单元,压缩率和速度都不错。 FastStringFuncs.pas  基于FastStrings.pas单元的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值