Description

给出一棵N个点的树
在树上进行随机点分治,每个节点需要你确定一个不超过K的权值,点分树上的父亲的权值严格小于儿子的。
问有多少种不同的结果,两种结果不同当且仅当某一个节点在点分树上的父亲不同或者它们的权值不同
1<=n<=50,1<=K<=15

Solution

直接按照题意模拟,N基本上都要存在于指数中

考虑转化题意。
点分治是有着严格的大小关系的,不妨倒过来想,确定了权值,也就确定了这棵点分树(在原树的某个子树内找到权值最小的并且将它作为分治中心)

那么当当前分治区域内权值最小的不止一个的时候就不合法了。

于是转而考虑相同的权值之间的关系,可以看出一种方案合法当且仅当对于所有权值相等的点对x,y,它们在原树上的最短路上至少有一个点权值小于它们(否则就不能将它们分开)

此时就可以DP了

先随便定个根
设Fi,S表示做到第i个节点,S是一个二进制状态,表示i的子树中到i有哪些权值,这些权值与i之间还没有比它小的数,S的每一位表示一种权值

考虑转移
枚举当前点的权值x,那么转移过来的状态中x这一位必为0,转移后的状态x后面的为必全为0,x这一位必为1
并且子树之间合并起来的时候子树的状态x这一位后面可以任意,并且x前面的所有位不能有交集(否则就没有小于它们的了)

可以用类似于前缀和之类的东西搞搞,然后可以用枚举补集的子集之类的优化。
最后的答案就是根节点所有状态加起来

复杂度O(NK3K),注意要加一些转移优化

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 52
#define M 33000
#define LL long long
#define mo 998244353
using namespace std;
LL f[N][M],g[N][17][M],s1[N][17][M];
int n,m,lim,nt[2*N],dt[2*N],fs[N],cf[17];
void link(int x,int y)
{
nt[++m]=fs[x];
dt[fs[x]=m]=y;
}
void dp(int k,int fa)
{
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p==fa) continue;
dp(p,k);
}
int q=0;
fo(j,0,lim-1) g[0][j][0]=1;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p==fa) continue;
fo(j,0,lim-1) fo(l,0,cf[j]-1) g[q+1][j][l]=0;
fo(j,0,lim-1)
{
fo(l,0,cf[j]-1)
{
if(!g[q][j][l]) continue;
int lv=(cf[j]-1)^l;
for(int x=lv;x;x=(x-1)&lv) g[q+1][j][l^x]=(g[q+1][j][l^x]+g[q][j][l]*s1[p][j][x])%mo;
g[q+1][j][l]=(g[q+1][j][l]+g[q][j][l]*s1[p][j][0])%mo;
}
}
q++;
}
fo(j,0,lim-1)
{
fo(l,0,cf[j]-1)
{
(f[k][l+cf[j]]+=g[q][j][l])%=mo;
}
}
fo(l,0,cf[lim]-1)
{
fo(j,0,lim-1)
{
if(cf[j]&l) continue;
(s1[k][j][l&(cf[j]-1)]+=f[k][l])%=mo;
}
}
}
int main()
cin>>n>>lim;
cf[0]=1;
fo(i,1,16) cf[i]=cf[i-1]*2;
fo(i,1,n-1)
{
int x,y;
scanf("%d%d",&x,&y);
link(x,y),link(y,x);
}
dp(1,0);
LL s=0;
fo(i,0,cf[lim]-1) (s+=f[1][i])%=mo;
printf("%lld\n",s);
}