4.13 省选模拟赛 树 树形dp 卷积 NTT优化dp.

avatar
avatar

考试的时候 看到概率 看到期望我就怂 推了一波矩阵树推自闭了 发现 边权点权的什么也不是。

想到了树形dp 维护所有边的断开情况 然后发现数联通块的和再k次方过于困难。

这个时候 应该仔细观察一下 和再k次方之后会出现什么 容易发现是一个类似隔板法的东西。

也就是 选出k个点的集合 集合可重 代价为点权之积.

只需要把所有的情况都做出来就行了。

至于联通块考虑一个一个统计贡献。

这也就是说 对于每一个联通块来说我们指定一个根节点来统计 要不然会算重。

不难发现以每个点的子树内部为联通块 可以不重不漏的计算。

设状态 f[i][j]表示 i所在的连通块选出了j个点权的点权积之和.

容易发现转移 (f[i][j]=(1-p)cdot f[i][j]+sum_{k=0}{j}f[x][k]cdot f[tn][j-k]cdot C(j,k))

为什么 后面要乘一个组合数 显然考虑将k次方展开之后 这其实是一个排列 那么方案数为 j!。

但是 f[x][k]中这k个数已经算了排列的数量了 同理f[tn][j-k]也是 所以可以把他们看成标号相同的点 所以各自要除以各自的阶乘。

显然每个点对答案的贡献为 (1-pfa)*f[x][k].

容易发现转移是一个卷积形式的 NTT优化即可。

const int MAXN=2050,G=3;
int n,k,len,ans,lim;
//int f[MAXN][MAXN]; f[i][j]表示以i为根的子树内 i所在的连通块中选出了j项的权值积之和.
//有 f[i][j]=(1-p)f[i][j]+f[tn][x]*f[i][j-x]*C(j,x) 显然是一个卷积NTT优化.
int f[MAXN][MAXN],b[MAXN],rev[MAXN],g[MAXN];
int fac[MAXN],inv[MAXN],A[MAXN],B[MAXN];
int lin[MAXN<<1],ver[MAXN<<1],nex[MAXN<<1],e[MAXN<<1];
inline int ksm(int b,int p){int cnt=1;while(p){if(p&1)cnt=(ll)cnt*b%mod;p=p>>1;b=(ll)b*b%mod;}return cnt;}
inline void add(int x,int y,int z)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
	e[len]=z;
}
inline void NTT(int *a,int op)
{
	rep(0,lim-1,i)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int len=2;len<=lim;len=len<<1)
	{
		int wn=ksm(G,op==1?(mod-1)/len:mod-1-(mod-1)/len);
		int mid=len>>1;
		for(int j=0;j<lim;j+=len)
		{
			int d=1;
			for(int i=0;i<mid;++i)
			{
				int x=a[i+j],y=(ll)a[i+j+mid]*d%mod;
				a[i+j]=(x+y)%mod;a[i+j+mid]=(x-y+mod)%mod;
				d=(ll)d*wn%mod;
			}
		}
	}
	if(op==-1)
	{
		int inv=ksm(lim,mod-2);
		rep(0,lim-1,i)a[i]=(ll)a[i]*inv%mod;
	}
}
inline void mul(int *a,int *b)
{
	rep(0,lim-1,i)
	{
		A[i]=(ll)a[i]*inv[i]%mod;
		B[i]=(ll)b[i]*inv[i]%mod;
	}
	NTT(A,1);NTT(B,1);
	rep(0,lim-1,i)g[i]=(ll)A[i]*B[i]%mod;
	NTT(g,-1);
}
inline void dp(int x,int fa,int fp)
{
	f[x][0]=1;
	rep(1,k,i)f[x][i]=((ll)f[x][i-1]*b[x])%mod;
	go(x)
	{
		if(tn==fa)continue;
		dp(tn,x,e[i]);
		mul(f[x],f[tn]);
		rep(0,k,j)
		{
			f[x][j]=(ll)(1-e[i]+mod)*f[x][j]%mod;
			f[x][j]=((ll)f[x][j]+(ll)g[j]*fac[j]%mod*e[i]%mod)%mod;
		}
	}
	ans=(ans+(ll)f[x][k]*(1-fp+mod)%mod)%mod;
}
int main()
{
	freopen("1.in","r",stdin);
	get(n);get(k);
	rep(1,n,i)get(b[i]);
	rep(2,n,i)
	{
		int get(x);int get(y);
		int get(a);int get(b);
		a=(ll)a*ksm(b,mod-2)%mod;
		add(x,y,a);add(y,x,a);
	}
	fac[0]=1;
	rep(1,k,i)fac[i]=(ll)fac[i-1]*i%mod;
	inv[k]=ksm(fac[k],mod-2);
	fep(k-1,0,i)inv[i]=(ll)inv[i+1]*(i+1)%mod;
	lim=1;while(lim<=k+k)lim=lim<<1;
	rep(1,lim-1,i)rev[i]=rev[i>>1]>>1|((i&1)?lim>>1:0);
	dp(1,0,0);put(ans);return 0;
}
原文地址:https://www.cnblogs.com/chdy/p/12697839.html