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\n",(ans+mod)%mod*n%mod);
fclose(stdin);
fclose(stdout);
return 0;
}