day1T2 osmanthus 题解
Rainbow_qwq · · 题解
我觉得这是 adhoc 好题啊,但是放在场上就区分上去了一些会猜
下面来复原一下考场想法。
称
题目的限制很奇怪,首先思考一下原树对最终树形成了什么限制。
经过手玩得出的结论是:新点似乎只能插在原树的某条边上,或者塞在原树节点下面,那么原树是最终树的一棵虚树。
那新点形成若干个子树、若干条链,能不能设计一个子树合并的 dp 呢?试了试,貌似不太行。
那就从部分分一个一个考虑。先考虑
子树合并不太行时因为限制了每一个
我们的限制是
考虑把
发现形式一下子变得比较好看,考虑一下
此时要求
那么插入点
那么
当然有的人暴力找规律就找出这一步了。
用同样的思想考虑
考虑从小到大一个一个插入点。此时不一样的是,一个点
也就是可以在现在树上的一条边上衍生出一个叶子,新加两个点,一个点是
再形式化的描述一下,发现我们考虑的正是
也就是说,空白需要填的点只有
那就可以进行状压 dp,设
转移有以下几种:
- 将
i+1 插入在边上或挂在点下面,系数2size-1 。 - 将
i+1 从一条边上衍生出来(加两个点),加入一个空白点,系数size-1 。 - 用
i+1 填上一个空白点。
虚树节点
时间复杂度 ull 加起来一起取模卡常。
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
#define ull unsigned long long
#define mod 1000000007
#define maxn 200005
#define inf 0x3f3f3f3f
int n,m,k,fa[maxn];
ull f[1<<10|5],g[1<<10|5];
int popc[1<<10|5];
void work()
{
n=read(),m=read(),k=read();
For(i,2,n)fa[i]=read();
if(k==0){
ull res=1;
For(i,n,n+m-1)res=res*(i*2-1)%mod;
cout<<res<<"\n";
return;
}
memset(f,0,sizeof f),f[0]=1;
int mxs=1<<k;
For(i,n,n+m-1){
For(s,(1<<(k-1)),(1<<k)-1)
g[(s<<1)&(mxs-1)]+=f[s];
For(s,0,(1<<(k-1))-1){
Rep(j,k-2,0)
if(s>>j&1) g[(s^(1<<j))<<1]+=f[s];
g[s<<1]+=f[s]*((i+popc[s])*2-1);
g[s<<1|1]+=f[s]*(i+popc[s]-1);
}
For(s,0,mxs-1) f[s]=g[s]%mod,g[s]=0;
}
cout<<f[0]<<"\n";
}
signed main()
{
For(i,1,(1<<10)-1)popc[i]=popc[i>>1]+(i&1);
read();int T=read();
while(T--)work();
return 0;
}