Loading

/ 7月/ 用一首诗向过去诀别

7.7 T3

statement

有一棵树,有点权 \(v_x\) ,每次可以缩起来一条边,新点的点权为 \(v_xv_y+1\),求所有操作顺序最后得到点的点权和。

\(n\leq 2000\)

solution

考虑组合意义,点权相当于可以选择相乘或者选择变成 \(1\),考虑我们用若干连通块表示,如果选择相乘就不合并连通块,否则就合并连通块,那么最终大小 \(=1\) 的连通块贡献就是 \(v_x\)\(>1\) 的连通块贡献就是 \(1\) ,乘起来就是答案。

那么直接对连通块划分计数,我们需要从每个连通块内选择出一条边作为最后一条边,然后要求其他边的合并顺序在这条边之前,对于连接不同连通块的边,要求连接的两个连通块的最后一条边的顺序在这条边之前,那么我们把这些顺序要求连边,就等价于计数拓扑序个数。

注意到连出的边忽略掉方向后变成了一棵树,这个经典做法是:把向上的边容斥成向下的,然后变成若干森林再进行外向树的拓扑序计数,在本题中需要记录当前树的子树大小以及当前连通块的大小,复杂度 \(O(n^3)\),链上的时候可以把每种长度的连通块预处理一下做到 \(O(n^2)\)

然后好像做不到 \(O(n^2)\),因此我们要换个做法计算拓扑序个数。

我们把每个连通块的最后一条边称作特殊边。

考虑直接设 \(f_{i,j}\) 表示只考虑子树内的边,当前连通块已经有了特殊边,且特殊边在这些边里排名为 \(j\) 的方案数,\(g_{i,j}\) 表示只考虑子树内的边,当前连通块还没有特殊边,且假设特殊边在这些边里排名为 \(j\) 的方案数(相当于提前把特殊边的位置留出来)。

这样设状态的好处是 \(f\)\(g\) 合并的时候只需要把特殊边前后分别合并就好,复杂度就是对了,而合并不同连通块的时候可以预处理一下前缀和,总的来说可以通过一些分类讨论做到 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 5020;
template <typename T>inline void read(T &x)
{
    x=0;char c=getchar();bool f=0;
    for(;c<'0'||c>'9';c=getchar())f|=(c=='-');
    for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+(c-'0');
    x=(f?-x:x);
}
typedef long long LL;
int n,W[N];
const int mod = 998244353;
inline int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(int a,int b){return 1ll*a*b%mod;}
vector<int> T[N];
int C[N][N];
void init(int n)
{
	for(int i=0;i<=n;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
		C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	}
}
int f[N][N],g[N][N],h[N],siz[N],F[N],G[N];
inline int mk(int i,int j)
{
	if(i<0||j<0)return 0;
	return C[i+j][i];
}
int B[N];
inline int val(int a,int b,int c,int d){return mul(mk(c,d),mk(a-c,b-d));}
void dfs(int x,int pre)
{
	siz[x]=1;
	for(int y:T[x])if(y^pre)
	{
		dfs(y,x);
		siz[x]+=siz[y];
	}
	h[x]=W[x];
	int c=0;
	g[x][0]=1;
	for(int y:T[x])if(y^pre)
	{
		int H=h[x];h[x]=0;
		for(int i=0;i<=siz[y];i++)B[i]=0;
		int s=0;
		for(int j=siz[y]-1;j>=0;j--)s=add(s,f[y][j]),B[j]=s;
		h[x]=add(h[x],mul(mul(H,mul(h[y],siz[y])),mk(c,siz[y])));
		for(int i=0;i<siz[y];i++)if(f[y][i])
		h[x]=add(h[x],mul(mul(H,mul(f[y][i],i+1)),mk(c,siz[y])));
		for(int i=0;i<=c;i++)
		{
			G[i]=g[x][i];F[i]=f[x][i];
			g[x][i]=f[x][i]=0;
		}
		for(int i=0;i<=c;i++)
		{
			if(G[i])
			{
				for(int j=0;j<=siz[y]-1;j++)//G*H->G
				g[x][i+j+1]=add(g[x][i+j+1],mul(mul(G[i],mul(i+1,h[y])),val(c+1,siz[y]-1,i+1,j))); 
				s=0;
				for(int j=1;j<=siz[y];j++)//G*F->G
				{
					s=add(s,B[j-1]);
					g[x][i+j]=add(g[x][i+j],mul(mul(G[i],s),val(c,siz[y],i,j)));
				}
				for(int j=0;j<=siz[y]-1;j++)//G*F->F
				{
					f[x][i+j]=add(f[x][i+j],mul(mul(G[i],f[y][j]),mul(val(c,siz[y]-1,i,j),siz[y]-1-j-1+1)));
					g[x][i+j]=add(g[x][i+j],mul(mul(G[i],g[y][j]),mul(val(c,siz[y],i,j),siz[y]-1-j+1)));
					f[x][i+j]=add(f[x][i+j],mul(mul(G[i],g[y][j]),val(c,siz[y]-1,i,j)));
				}
			}
			if(F[i])
			{
				for(int j=0;j<=siz[y]-1;j++)//F*H->F
				f[x][i+j+1]=add(f[x][i+j+1],mul(mul(F[i],mul(i+1,h[y])),val(c-1+1,siz[y]-1,i+1,j))); 
				s=0;
				for(int j=1;j<=siz[y];j++)//F*F->F
				{
					s=add(s,B[j-1]);
					f[x][i+j]=add(f[x][i+j],mul(mul(F[i],s),val(c-1,siz[y],i,j)));
				}
				for(int j=0;j<=siz[y]-1;j++)//F*G->F
				{
					f[x][i+j]=add(f[x][i+j],mul(mul(F[i],g[y][j]),mul(val(c-1,siz[y],i,j),siz[y]-1-j+1)));
				}
			}
		}
		c+=siz[y];
	}
}
int main()
{
	read(n);
	for(int i=1;i<=n;i++)read(W[i]);
	for(int i=1;i<n;i++)
	{
		int x,y;
		read(x);read(y);
		T[x].push_back(y);
		T[y].push_back(x);
	}
	init(n);
	dfs(1,0);
	int ans=h[1];
	for(int i=0;i<=n;i++)ans=add(ans,f[1][i]);
	cout<<ans;
	return 0;
} 
posted @ 2024-07-08 22:57  Larunatrecy  阅读(25)  评论(0编辑  收藏  举报