[航海协会]树
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[航海协会]树相关的知识,希望对你有一定的参考价值。
树
题目概述
题解
首先看到这个题,我们应该比较容易得到一个状压的做法。
就是我们定义
d
p
i
,
j
dp_i,j
dpi,j表示节点
i
i
i的
m
e
x
\\rm mex
mex为
j
j
j的方案数。
显然,一个点的权值最后是极其有限的,显然它是不能够超过它的出度的。
那我们就可以考虑每次状压记录当前节点的已加入儿子已经存在哪些不超过度数上限的权值,最后把最小的没有加入的权值找到就行了。
但这样可能会出现一种情况,就是类似菊花图的,度数很多的情况,那不炸麻。
我们不妨猜测一下,如果它的度数很多,这个点的上限不会太高。
但好像不是这样的,因为它可能既有非叶节点的儿子也有叶节点的儿子,这两者加在一起,是非常恐怖的。
不过叶节点这东西好搞,它为每个权值的情况数都是
1
1
1。
我们容易通过这个性质再想到一个新的状压做法,记录
g
i
,
S
,
j
g_i,S,j
gi,S,j表示我们使用
S
S
S集合中的非叶节点与
j
j
j个叶节点,填充了
[
0
,
i
]
[0,i]
[0,i]的所有位置。
这样的话,我们只要转移到某一位的时候,没有往里面填任何的数,那么它的权值就是这个了,把剩下的没填的数随便填到更大的数里面即可。
但如果我们非叶节点的儿子很多呢?
没关系,在这些儿子中上限高的肯定很少。
我们可以再给他套上一个根号分治,先对于上限小的儿子,采用我们上面的方法,状压得到小范围的每个选择状态的方案数。
然后再对这些方案每个用我们第二个状压方法转移一次。
这样就能够求出所有的
d
p
i
,
j
dp_i,j
dpi,j了。
时间复杂度 O ( 2 2 n n 2 ) O\\left(2^\\sqrt2nn^2\\right) O(22nn2)。
源码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXM (1<<15)+5
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
const int INF=0x3f3f3f3f;
const int mo=998244353;
template<typename _T>
void read(_T &x)
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9')if(s=='-')f=-1;s=getchar();
while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
x*=f;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;return t;
int n,head[205],tot,deg[205],dp[205][205],up[205],sta[205],stak;
int g[2][MAXM],h[MAXM][205],fac[205],inv[205],ff[205],suf[205][205];
int S[205][205],C[205][205],tmp[2][MAXM][205][2],gp[205];bool leaf[205];
struct edgeint to,nxt;e[405];
void addEdge(int u,int v)e[++tot]=(edge)v,head[u];head[u]=tot;
bool cmp(int x,int y)return up[x]<up[y];
void dosaka(int u,int fa)
for(int i=head[u];i;i=e[i].nxt)
if(e[i].to^fa)dosaka(e[i].to,u);
stak=0;int num=0;
for(int i=head[u];i;i=e[i].nxt)
int v=e[i].to;if(v==fa)continue;
deg[u]++;num+=(leaf[v]);
if(!leaf[v])sta[++stak]=v;
if(!deg[u])leaf[u]=1;return ;
sort(sta+1,sta+stak+1,cmp);int mid=0,minn=stak,nw=0;
for(int i=1;i<=stak;i++)nw=min(nw+1,up[sta[i]]+1);up[u]=nw+num;
for(int i=1;i<=stak;i++)
if(up[sta[i]]+stak-i<minn)
minn=up[sta[i]]+stak-i,mid=i;
int upp=up[sta[mid]],lim=(1<<upp+1),lm=(1<<stak-mid),now=0,las=1;
for(int i=0;i<lim;i++)g[0][i]=g[1][i]=0;g[now][0]=1;
for(int i=1;i<=mid;i++)
swap(now,las);int v=sta[i];
for(int j=0;j<=up[v];j++)if(dp[v][j])
for(int k=0;k<lim;k++)
Add(g[now][k|(1<<j)],1ll*dp[v][j]*g[las][k]%mo,mo);
for(int k=0;k<lim;k++)g[las][k]=0;
for(int i=0;i<lim;i++)g[0][i]=g[now][i];
for(int i=0;i<lim;i++)
const int tp=g[0][i];if(!tp)continue;h[0][0]=tp;
for(int j=0;j<=up[u];j++)
now=0,las=1;
for(int k1=0;k1<lm;k1++)
for(int k2=0;k2<=min(num,j);k2++)
tmp[now][k1][k2][0]=h[k1][k2],h[k1][k2]=0;
for(int l=mid+1;l<=stak;l++)
int v=sta[l],gg=(1<<l-mid-1);if(up[v]<j)continue;swap(now,las);
for(int k1=0;k1<lm;k1++)
for(int k2=0;k2<=min(num,j);k2++)
for(int k3=0;k3<2;k3++)if(tmp[las][k1][k2][k3])
if(!(k1&gg))Add(tmp[now][k1|gg][k2][1],1ll*tmp[las][k1][k2][k3]*dp[v][j]%mo,mo);
Add(tmp[now][k1][k2][k3],tmp[las][k1][k2][k3],mo);tmp[las][k1][k2][k3]=0;
for(int k1=0;k1<lm;k1++)
for(int k2=0;k2<=min(num,j);k2++)
Add(h[k1][k2],tmp[now][k1][k2][1],mo);
if(k2<num)Add(h[k1][k2+1],tmp[now][k1][k2][1],mo),
Add(h[k1][k2+1],tmp[now][k1][k2][0],mo);
int rs=tmp[now][k1][k2][0];tmp[now][k1][k2][0]=tmp[now][k1][k2][1]=0;
if(!rs)continue;if(j<=upp&&(i&(1<<j)))Add(h[k1][k2],rs,mo);continue;
for(int k3=mid+1;k3<=stak;k3++)if(!((k1>>k3-mid-1)&1))
rs=1ll*suf[sta[k3]][j+1]*rs%mo;
Add(gp[k2],rs,mo);
for(int k1=0;k1<=min(num,j);k1++)if(gp[k1])
for(int k2=0;k2<=n-j;k2++)
Add(dp[u][j][航海协会]树