正题
题目链接:https://ac.nowcoder.com/acm/contest/7745/E
题目大意
给出nnn个点的一棵树,每个点有一个选择权重aia_iai(有ai∑i=1nai\frac{a_i}{\sum_{i=1}^na_i}∑i=1naiai的概率被选择)。
然后有一个序列www。随机选择两次点(可以相同)若它们之间距离为LLL,那么困难值为wLw_LwL
求期望困难值。
1≤n≤105,0≤wi≤1081\leq n\leq 10^5,0\leq w_i\leq 10^81≤n≤105,0≤wi≤108
解题思路
设pip_ipi表示选择iii的概率那么就是求
∑i=1n∑j=1npipjwdis(i,j)\sum_{i=1}^n\sum_{j=1}^np_ip_jw_{dis(i,j)}i=1∑nj=1∑npipjwdis(i,j)
看起来很点分治就上点分治吧
怎么合并两个子树的距离,设uiu_iui表示子树111中深度为iii的概率和,viv_ivi则表示子树222中的。
那么就有
ans=∑i=1∑j=1uiviwi+j=∑i=1wi∑j=1ujvi−jans=\sum_{i=1}\sum_{j=1}u_iv_iw_{i+j}=\sum_{i=1}w_{i}\sum_{j=1}u_jv_{i-j}ans=i=1∑j=1∑uiviwi+j=i=1∑wij=1∑ujvi−j
看起来很卷积就上NTT\text{NTT}NTT吧
做起来比较麻烦,题解告诉我们可以直接计算整个树的然后再分别减去每个子树内的。
时间复杂度O(nlog2n)O(n\log^2 n)O(nlog2n)
这下一雪我半年前考场调了半天长剖+NTT的前耻了
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=4e5+10,P=998244353;
struct node{ll to,next;
}a[N<<1];
ll n,l,ans,root,num,mx,tot,ls[N],p[N],w[N];
ll r[N],g[N],siz[N],f[N],x[N];
bool v[N];
ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%P;x=x*x%P;b>>=1;}return ans;
}
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void NTT(ll *f,ll op){for(ll i=0;i<l;i++)if(i<r[i])swap(f[i],f[r[i]]);for(ll p=2;p<=l;p<<=1){ll len=p>>1,tmp=power(3,(P-1)/p);if(op==-1)tmp=power(tmp,P-2);for(ll k=0;k<l;k+=p){ll buf=1;for(ll i=k;i<k+len;i++){ll tt=buf*f[i+len]%P;f[i+len]=(f[i]-tt+P)%P;f[i]=(f[i]+tt)%P;buf=buf*tmp%P;}}}if(op==-1){ll invn=power(l,P-2);for(ll i=0;i<l;i++)f[i]=f[i]*invn%P;}return;
}
void GetL(ll n){l=1;while(l<n)l<<=1;for(ll i=0;i<l;i++)r[i]=(r[i>>1]>>1)|((i&1)?(l>>1):0);return;
}
void groot(ll x,ll fa){siz[x]=1;g[x]=0;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa||v[y])continue;groot(y,x);siz[x]+=siz[y];g[x]=max(g[x],siz[y]);}g[x]=max(g[x],num-siz[x]);if(g[x]<g[root])root=x;return;
}
void calc(ll x,ll fa,ll dep){(f[dep]+=p[x])%=P;mx=max(mx,dep);for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa||v[y])continue;calc(y,x,dep+1);}return;
}
void del(){for(ll i=0;i<=mx;i++)f[i]=0;mx=0;return;
}
void fuc(ll n,ll z){GetL(2*n);for(ll i=0;i<l;i++)x[i]=f[i];NTT(x,1);for(ll i=0;i<l;i++)x[i]=x[i]*x[i]%P;NTT(x,-1);for(ll i=0;i<l;i++)(ans+=z*w[i]*x[i]%P)%=P;return;
}
void solve(ll x){v[x]=1;ll tal=num;calc(x,x,0);fuc(mx+1,1);del();for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(v[y])continue;calc(y,x,1);fuc(mx+1,-1);del();}for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(v[y])continue;num=(siz[y]>siz[x])?(tal-siz[x]):siz[y];root=0;groot(y,x);solve(root);}return;
}
signed main()
{scanf("%lld",&n);ll s=0;for(ll i=1;i<=n;i++)scanf("%lld",&p[i]),s+=p[i];for(ll i=1;i<=n;i++)p[i]=p[i]*power(s,P-2);for(ll i=0;i<n;i++)scanf("%lld",&w[i]);for(ll i=1;i<n;i++){ll x,y;scanf("%lld%lld",&x,&y);addl(x,y);addl(y,x);}num=n;g[0]=1e9;groot(1,1);solve(1);printf("%lld\n",(ans+P)%P);return 0;
}