题解:AT_arc130_d [ARC130D] Zigzag Tree
题意
给定一棵树,统计满足条件的点权序列的数量:
- 点权为 \(1\) 到 \(n\) 的排列。
- 对于任意点 \(u\),其点权要么大于所有邻居,要么小于所有邻居。
\(n \leq 3000\)。
题解
我们称点权大于所有邻居的点为大点,点权小于所有邻居的点为小点。
一个点一定不会同时成为大小点,同一条边连接的两个点类型不同。
设 \(f_{u,i,0/1}\) 表示以 \(u\) 为根的子树,\(u\) 在子树内排名为 \(i\),点 \(u\) 为小点或大点的方案数。
发现一定有 \(f_{u,i,0}=f_{u,i,1}\),因此可以把最后一维去掉。
类似树上背包的转移,设 \(g_i=f_{u,i}\) 为儿子 \(v\) 向 \(u\) 转移前的 \(f_u\)。
对于每个 \(f_{u,j}\),枚举加入 \(v\) 子树前 \(u\) 的排名 \(x\),有:
\[f_{u,j}= \sum_{\max(j-sz_v,1) \leq x \leq \min(j-1,sz_u)} C_{j-1}^{x-1} C_{sz_u+sz_v-j}^{sz_u-x}g_x \sum_{1 \leq y \leq j-x} f_{v,sz_v+1-y}
\]
- 这篇题解的写法上,上文式子中的 \(sz_u\) 并没有算入 \(sz_v\)。
- 第一个组合数:总共有 \(j-1\) 个小于 \(u\) 的排名的,选出 \(x-1\) 个给加入 \(v\) 子树前的点。
- 第二个组合数,同理,总共有 \(sz_u+sz_v-j\) 个大于 \(u\) 的排名的。
- 第二个求和中 \(y\) 表示枚举 \(v\) 子树中 \(v\) 的排名,\(sz_v+1-y\) 是做了一个翻转,因为我们把最后一维去掉了。
- 注意 \(x,y\) 的枚举边界,边界不对的话复杂度就会错。和树上背包是同样的道理。
容易发现第二个求和可以前缀和优化:
\[f_{u,j}= \sum_{\max(j-sz_v,1) \leq x \leq \min(j-1,sz_u)} C_{j-1}^{x-1} C_{sz_u+sz_v-j}^{sz_u-x}g_x pre_{j-x}
\]
最终的答案就是 \(\sum f_{1,i}\)。
代码
完整代码见提交记录。
void dfs(int u,int fa)
{
for(int v:ve[u])
if(v^fa) dfs(v,u);
sz[u]=1;
f[u][1]=1ll;
for(int v:ve[u])
{
if(v==fa) continue;
memcpy(g,f[u],sizeof(ll)*(sz[u]+1));
memset(f[u],0,sizeof(ll)*(sz[u]+sz[v]+1));
For(j,1,sz[v])
pre[j]=(pre[j-1]+f[v][sz[v]+1-j])%mod;
For(j,1,sz[u]+sz[v])
For(x,max(j-sz[v],1),min(sz[u],j-1)){
ll tmp=C(j-1,x-1)*C(sz[u]+sz[v]-j,sz[u]-x)%mod;
qadd(f[u][j],tmp*g[x]%mod*pre[j-x]%mod);
}
sz[u]+=sz[v];
}
}

浙公网安备 33010602011771号