6541. 【GDOI2020模拟4.4】Permutation

题目描述


题解

迫真签到题

前几天做过类似的,只不过要求的是相邻的lca,所以要n^3考虑具体每一段

对于这题不行

显然每个子树内的段=边权/2,并且合并时要求不能合并相同子树内的段

容斥一下,系数是(-1)^合并相同子树内的段再乘上组合数

假设当前不为整棵树的根,设第子树i原有p[i]段,合并后变成q[i]段,最后再合并成k段

则贡献为

(huge frac{sum q[i]+1}{prod q[i]!}C_{(sum q[i]+1)-1}^{k-1}prod(-1)^{p[i]-q[i]}C_{p[i]-1}^{q[i]-1})

就是把合并完的随便放再合并成k段,注意要考虑当前节点

手玩一下发现当合并段数>0时容斥系数之和=0,因此容斥后的会在每个节点处合并成真正的方案数

把式子拆开背包

如果当前是整棵树的根就硬点为第一段,最后乘上n的轮换

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define C(n,m) (jc[n]*Jc[m]%mod*Jc[(n)-(m)]%mod)
#define ll long long
#define file
using namespace std;

int a[10001][3],ls[5001],size[5001],n,mod,Mod,i,j,k,l,len;
ll f[5001],g[5001],G[5001],jc[5001],Jc[5001],w[5001],ans;

ll qpower(ll a,int b) {ll ans=1;while (b) {if (b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;} return ans;}
void New(int x,int y,int z) {++len;a[len][0]=y;a[len][1]=ls[x];ls[x]=len;a[len][2]=z;}

void dfs(int Fa,int t,int Ls)
{
	int i;
	ll s;
	
	for (i=ls[t]; i; i=a[i][1])
	if (a[i][0]!=Fa)
	dfs(t,a[i][0],a[i][2]);
	
	memset(g,0,sizeof(g));g[1]=size[t]=1;
	for (i=ls[t]; i; i=a[i][1])
	if (a[i][0]!=Fa)
	{
		memset(G,0,(size[t]+size[a[i][0]]+1)*8);
		
		fo(j,1,size[t])
		{
			fo(k,1,a[i][2])
			G[j+k]=(G[j+k]+g[j]*f[a[i][0]]%mod*((a[i][2]-k)&1?-1:1)*C(a[i][2]-1,k-1)%mod*Jc[k])%mod;
		}
		size[t]+=size[a[i][0]];
		
		memcpy(g,G,(size[t]+1)*8);
	}
	
	if (Fa)
	{fo(i,Ls,size[t]) f[t]=(f[t]+g[i]*jc[i]%mod*C(i-1,Ls-1))%mod;}
	else
	{fo(i,1,size[t]) ans=(ans+g[i]*jc[i-1]%mod)%mod;}
}

int main()
{
	freopen("permutation.in","r",stdin);
	#ifdef file
	freopen("permutation.out","w",stdout);
	#endif
	
	scanf("%d%d",&n,&mod),Mod=mod-2;
	jc[0]=jc[1]=Jc[0]=Jc[1]=w[1]=1;
	fo(i,2,n) scanf("%d%d%d",&j,&k,&l),l/=2,New(j,k,l),New(k,j,l),w[i]=mod-w[mod%i]*(mod/i)%mod,jc[i]=jc[i-1]*i%mod,Jc[i]=Jc[i-1]*w[i]%mod;
	
	dfs(0,1,-1);
	
	printf("%lld
",(ans+mod)%mod*n%mod);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/12684324.html