题解:超级树
题解:超级树
Description:
基本思路:
这可以说是本次模拟测试中最难的一道了,无人考场AC
这道题用的是DP,状态空间定义真的是鬼能想出来啊。。。
记c[i][j]表示一棵k-超级树同时具有j条不相交的道路的方案数
这魔一般的状态空间属实让我琢磨了很久。那么就会产生以下,五个方程:
1: c[i+1][l+r]=c[i+1][l+r]+num
2: c[i+1][l+r+1]=c[i+1][l+r+1]+num
3: c[i+1][l+r-1]=c[i+1][l+r-1]+num*l*r*2)
4: c[i+1][l+r]=c[i+1][l+r]+num*(l+r)*2
5: c[i+1][l+r-1]=c[i+1][l+r-1]+num*(l*(l-1)+r*(r-1))
l表示i+1–超级树里左子树有l条不相交边,右子树里有r条不相交边,num=c[i][l]*c[i][r]
对于方程一:就是只连上根节点而不把根节点算入路径的情况,一个简单的乘法原理
对于方程二:就是把根节点自己算作一条路径,不把他与左右子树的路径相连的情况,子树仍旧乘法原理,只是加上根节点一条路径变成l+r+1
对于方程三:就是将根节点连在左子树某条路径的终点上然后连右子树某条路径的起点,这样会使总的路径数减一(两条并一条嘛)在每一种大情况下,左子树中有l条路径供选右子树中有r条路径供选,所以是l*r,又可以是连左子树的起点连右子树的终点,所以乘二
对于方程四:就是根节点只连左子树中某条路径或只连右子树某条路径的情况,又因为可以连终点或起点,所以乘二
对于方程五:就是根节点同时连左(右)子树某条路径的起点与另一条路径的终点的情况,所以是l*(l-1)或r*(r-1)。
注意取模与减循环,不然会T
附上AC代码:
#include<bits/stdc++.h>
using namespace std;
namespace Decleration
{
#define ll long long
#define rr register
const int SIZE=304;
int k;
ll mod;
ll c[SIZE][SIZE];
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace Decleration;
int main()
{
c[1][0]=c[1][1]=1;
k=read(),mod=read();
ll num=1;
for(rr int i=1;i<k;i++)
{
for(rr int l=0;l<=k-i+2&&(i<=30?(l<=(1<<i)-1):1);l++)
for(rr int r=0;r<=k-i+2-l&&(i<=30?(r<=(1<<i)-1):1);r++)
{
num=c[i][l]*c[i][r]%mod;
c[i+1][l+r]=(c[i+1][l+r]+num+mod)%mod;//什么都不做
c[i+1][l+r+1]=(c[i+1][l+r+1]+num+mod)%mod;//把根作为单独的一条路径
c[i+1][l+r-1]=(c[i+1][l+r-1]+(num*l%mod)*r*2%mod+mod)%mod;//用根去连左(右)边一条路的起点再连右(左)边一条路的终点
c[i+1][l+r]=(c[i+1][l+r]+(num*(l+r)%mod)*2+mod)%mod;//根只连左边一条路径或只连右边一条路径
c[i+1][l+r-1]=(c[i+1][l+r-1]+num*(l*(l-1)%mod+r*(r-1)%mod)%mod+mod)%mod;//连接左边(右边)两条路径
}
}
printf("%lld\n",(c[k][1]+mod)%mod);
}