Title

Path Counting题解

原题链接

题目大意:给定一颗以 \(1\) 为根,深度为 \(n\) 的有根树(规定根的深度为 \(1\)),树上每一个深度为 \(i\) 的点都有 \(a_i\) 个儿子。对于每一个 \(k \in [1,2n-2]\) 求出长度为 \(k\) 的路径数量。

数据范围:\(2 \le n \le 5000\)\(2 \le a_i \le 10^9\)

路径分为两种,一种为两端点的LCA在路径中间(不在端点上),另一种为两端点的LCA在端点上,我们分别求解,然后将路径数量相加。

LCA在路径中间

我们考虑在LCA处统计路径数。

\(f_{i,j}\) 表示以深度为 \(i\) 的点为LCA的路径中,长度为 \(j\) 的有多少条。

我们考虑如何计算 \(f_{i,j}\),有一个非常显然的式子,就是枚举左右端点的深度,然后计算满足这两个端点的LCA深度为 \(i\) 的方案数,即:

\[\large f_{i,j}=\frac{a_i \times (a_i-1)} 2 \times \sum_{k=i+1}^{i+j-1}(\prod_{x=i+1}^{k-1} a_x \prod_{x=i+1}^{j-k-1} a_x) \]

式子的含义就是先选出该节点的两个儿子,然后从这两个儿子向下扩展路径,一个扩展 \(k-1\) 长度,另外一个扩展 \(j-k-1\),可以简单前缀积优化一下做到 \(O(N^3)\)

需要进行优化,注意到 \(f_{i,j}\) 式子右边的式子和 \(f_{i+1,j-2}\) 的很像,我们把这两个式子放在一起。

\[\large f_{i,j}=\frac{a_i \times (a_i-1)} 2 \times \sum_{k=i+1}^{i+j-1}(\prod_{x=i+1}^{k-1} a_x \prod_{x=i+1}^{j-k-1} a_x) \\ \large f_{i+1,j-2}=\frac{a_{i+1} \times (a_{i+1}-1)} 2 \times \sum_{k=i+2}^{i+j-2}(\prod_{x=i+2}^{k-1} a_x \prod_{x=i+2}^{j-k-3} a_x) \]

发现 \(f_{i,j}\) 的大部分贡献都是从 \(\frac{a_i \times (a_i-1)} 2 \times f_{i+1,j-2} \times \frac{2a_{i+1}}{a_{i+1}-1}\),除去 \(k=1\)\(k=i+j-1\) 的情况,这两种情况用前缀积算上就好了。

最后,\(f_{i,j}\) 乘上深度为 \(i\) 的点的数量就是这一部分对于答案的贡献了。

LCA在端点

这个更好做了,LCA在端点的路径中长度为 \(i\) 的只有可能在深度大于 \(i\) 的点取到,统计每个深度有多少个点即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
const int mod=1000000007;
int n;
int a[maxn];
int f[maxn][maxn*2],g[maxn*2],inv[maxn];
int power(int x,int y)
{
	int ans=1;
	while(y)
	{
		if(y&1)
			ans=1ll*ans*x%mod;
		y>>=1;
		x=1ll*x*x%mod;
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
		scanf("%d",&a[i]),inv[i]=power(a[i]-1,mod-2);
	f[n-1][2]=1ll*a[n-1]*(a[n-1]-1)/2%mod;
	for(int i=n-2;i>=1;i--)
	{
		for(int p=2*n-2;p>=2;p--)
			f[i][p]=1ll*a[i]*(a[i]-1)/2%mod*f[i+1][p-2]%mod*inv[i+1]%mod*2ll%mod*a[i+1]%mod;//从f[i+1][p-2]转移贡献
		//加上k=1和k=i+j-1的贡献
		f[i][2]=(f[i][2]+1ll*a[i]*(a[i]-1)/2%mod)%mod;
		int cnt=1;
		for(int p=3;p<=2*n-2;p++)
		{
			cnt=1ll*cnt*a[i+p-2]%mod;
			f[i][p]=(f[i][p]+1ll*a[i]*(a[i]-1)%mod*cnt%mod)%mod;
		}
	}
	int cnt=1,sum=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=2*n-2;j++)
			g[j]=(g[j]+1ll*cnt*f[i][j]%mod)%mod;//计算对答案的贡献
		sum=(sum+cnt)%mod;
		cnt=1ll*cnt*a[i]%mod;
	}
	cnt=1;int num=0;
	for(int i=1;i<n;i++)
		num=(num+cnt)%mod,g[i]=(0ll+g[i]+sum-num+mod)%mod,cnt=1ll*cnt*a[i]%mod;//LCA在端点的路径
	for(int i=1;i<=2*n-2;i++)
		printf("%d ",g[i]);
	puts("");
	
	return 0;
}
posted @ 2022-09-15 15:34  五百年前  阅读(30)  评论(0编辑  收藏  举报