牛客练习赛71E-神奇的迷宫【点分治,NTT】
正題
題目鏈接:https://ac.nowcoder.com/acm/contest/7745/E
題目大意
給出nnn個點的一棵樹,每個點有一個選擇權(quán)重aia_iai?(有ai∑i=1nai\frac{a_i}{\sum_{i=1}^na_i}∑i=1n?ai?ai??的概率被選擇)。
然后有一個序列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
解題思路
設(shè)pip_ipi?表示選擇iii的概率那么就是求
∑i=1n∑j=1npipjwdis(i,j)\sum_{i=1}^n\sum_{j=1}^np_ip_jw_{dis(i,j)}i=1∑n?j=1∑n?pi?pj?wdis(i,j)?
看起來很點分治就上點分治吧
怎么合并兩個子樹的距離,設(shè)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∑?ui?vi?wi+j?=i=1∑?wi?j=1∑?uj?vi?j?
看起來很卷積就上NTT\text{NTT}NTT吧
做起來比較麻煩,題解告訴我們可以直接計算整個樹的然后再分別減去每個子樹內(nèi)的。
時間復(fù)雜度O(nlog?2n)O(n\log^2 n)O(nlog2n)
這下一雪我半年前考場調(diào)了半天長剖+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; }總結(jié)
以上是生活随笔為你收集整理的牛客练习赛71E-神奇的迷宫【点分治,NTT】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NOI.AC#2007-light【根号
- 下一篇: 怎么设置微信支付(怎么设置微信支付顺序)