[JZOJ 5911] [NOIP2018模拟10.18] Travel 解题报告 (期望+树形DP)
題目鏈接:
http://172.16.0.132/senior/#contest/show/2530/1
題目:
? ? ? ? EZ同學家里非常富有,但又極其的謙虛,說話又好聽,是個不可多得的人才。
? ? ? ? EZ常常在假期環游世界,他準備去N(N<=100000)個國家之多,一些國家有航線連接,由于EZ同學有一定的強迫癥,任意兩個國家之間都能通過航路直接或間接到達,并且這樣的路徑僅有一種。(簡單來說,這些國家構成了一棵樹)
? ? ? ? ?由于EZ是C國人,因此將C國(1號國家)作為整棵樹的根
? ? ? ? 每個國家有一個旅游熱度A[i]和影響力D[i]。由于目的地有點多,為了避免選擇困難癥,他給每個國家設置了一個向往值F[i],它等于所有的A[j]之和,滿足i國在j國向C國走D[j]步的路徑上(經過一條航路算一步,i=j也會被統計,如果D[j]步超過了C國,則超出部分不用管)。
? ? ? ? ?LYD同學家里有礦,富有程度與EZ不相上下,但他卻在宅與現充間搖擺不定。某次機緣巧合,EZ外出旅游刺激了LYD,他決定也要開始旅游。為了避免又被判高重復率導致被取消資格,他將EZ的旅游地圖略微做了一點調整,每條航路將有一定的概率出現。
? ? ? ? ?現在他有Q個詢問,每次詢問某個國家所在的聯通塊(由于每條邊是一定概率出現,因此它所在的聯通塊可以是很多種)中所有國家的F[i]值的和的平方的期望(對998244353取模),以此來決定他旅游的目的地。但他極其厭惡繁瑣的計算,于是他找到了能算出圓周率并將它倒背下來的你,答應給你豐厚的報酬。家里沒礦,老爸也不是X達集團老總的你決定接受他的任務。
題外話:
這題...我查錯差不多4小時吧,這一次查錯發現了很多代碼應該注意的坑點,我無一例外踩了一個遍
題解:
注意1:注意!!!注意!!!期望和的平方不等于和的平方的期望
注意2:每個在EZ的地圖中是沒有出現概率的說法的,因此每個國家的f值與邊的出現概率無關
首先考慮怎么求f數組,對于每個國家在它自己打上一個+的標記,在它的 $d[i]+1$ 級祖先打上-的標記, 就可以直接子樹求和了。查詢某個位置的 $d[i]+1$ 級祖先可以在 DFS 的時候維護一 個棧,然后對于每個點直接訪問棧中相應位置即可,這也是線性的。因此這樣就能線性的求出$f$數組
考慮如何計算和的平方,我們不妨以詢問的國家作為根重新 DFS 一遍這棵樹,考慮 DP, 主要問題就是求兩個塊以一條出現概率為$ p$ 的邊合并時的答案。不妨令原本的塊期望和為$a$,并入的塊期望和為$b$,根據期望的線性可加性,那么答案是$p(a + b)^2 + (1 ? p)a^2 = a^2 + p(2ab +b ^2 )$
這樣對于每個節點,維護$k2$數組表示以它為根的子樹的期望和,維護$k3$數組表示以它為根的子樹的和平方的期望,根據上面那個式子我們就可以合并答案
由于每次詢問都要重新DFS,時間復雜度 $O(NQ)$,顯然不行(事實上這樣就30分)
其實并不需要對于每次詢問都重新 DFS 轉移,我們不妨仍然以1為根,那么 對于一個節點$i$,我們只需要知道$i$的子樹部分的$k2$和$k3$,以及$i$以外的部分的期望和以及和平方的期望,這樣就可以維護出i的任意一個兒子的上述信息,我們設$g[i]$表示除i的子樹之外的節點的和平方的期望,h[i]表示除i的子樹外的節點的期望和,$ans[i]=g[y]+2*h[y]*k2[y]+k3[y]$(注意不能寫成$ans[i]=(h[y]+k2[y])^2$,原因參考注意1)
這就是二次掃描與換根的思想
具體怎么從i轉移到$i$的兒子呢?我們給i的兒子一個順序,對第$j$個兒子維護前綴其他兒子的兩個信息,再維護后綴其他兒子的兩個,這樣第j個兒子的信息就相等于合并4部分,$h[x]$,前綴,合并,$f[x]$(代碼中表示為$a,b,c,d$)
至于我查錯中出現的坑點,一般僅限我的代碼,就不贅述了
#include<algorithm> #include<cstring> #include<cstdio> #include<iostream> using namespace std; typedef long long ll;const int N=4e5+15; const ll mo=998244353; const int S=1e7; int n,tot=1,tp; int h[N],d[N],sta[N],tt[N]; ll cha[N],a[N],k2[N],k3[N],p1[N],p2[N],p3[N],f[N],H[N],G[N],ans[N]; ll Pre2[N],Pre3[N],Bac2[N],Bac3[N]; struct E{int to,nxt;ll p; }e[N<<1]; inline int gc(){static char buf[S];static int len=0,pos=0;if (pos==len) pos=0,len=fread(buf,1,S,stdin);if (pos==len) exit(0);return buf[pos++]; } inline ll read(){char ch=gc();ll s=0,f=1;while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=gc();}return s*f; } void link(int u,int v,ll p) {e[++tot]=(E){v,h[u],p};h[u]=tot;} void dfs1(int x,int pre){sta[++tp]=x;cha[x]=(cha[x]+a[x])%mo;cha[sta[tp-d[x]-1]]=(cha[sta[tp-d[x]-1]]+(mo-a[x]%mo))%mo;for (int i=h[x],y;i;i=e[i].nxt){if ((y=e[i].to)==pre) continue;dfs1(y,x);}tp--; } void dfs2(int x,int pre){f[x]=cha[x];for (int i=h[x],y;i;i=e[i].nxt){if ((y=e[i].to)==pre) continue;dfs2(y,x);f[x]=(f[x]+f[y])%mo;} } void dfs3(int x,int pre){k2[x]=f[x];k3[x]=f[x]*f[x]%mo;for (int i=h[x],y;i;i=e[i].nxt){if ((y=e[i].to)==pre) continue;ll p=e[i].p;dfs3(y,x);k3[x]=(k3[x]+2*p%mo*k2[x]%mo*k2[y]%mo+p*k3[y]%mo)%mo;k2[x]=((k2[x]+k2[y])*p%mo+((1-p)%mo+mo)%mo*k2[x]%mo)%mo;} } void dfs4(int x,int pre){Pre2[0]=0;Pre3[0]=0;int j=0;for (int i=h[x],y;i;i=e[i].nxt){if ((y=e[i].to)==pre) continue;++j;tt[j]=i;ll p=e[i].p;Pre3[j]=(Pre3[j-1]+2*p%mo*Pre2[j-1]%mo*k2[y]%mo+p*k3[y]%mo)%mo;Pre2[j]=((Pre2[j-1]+k2[y])*p%mo+((1-p)%mo+mo)%mo*Pre2[j-1]%mo)%mo;}Bac2[j+1]=0;Bac3[j+1]=0;for (;j;j--){int i=tt[j];int y=e[i].to;ll p=e[i].p;Bac3[j]=(Bac3[j+1]+2*p%mo*Bac2[j+1]%mo*k2[y]%mo+p*k3[y]%mo)%mo;Bac2[j]=((Bac2[j+1]+k2[y])%mo*p%mo+((1-p)%mo+mo)%mo*Bac2[j+1]%mo)%mo;ll a=H[x],b=Pre2[j-1],c=Bac2[j+1],d=f[x];ll aa=G[x],bb=Pre3[j-1],cc=Bac3[j+1],dd=f[x]*f[x]%mo;G[y]=aa;//一個個合并,不能一起,參考注意1 H[y]=a;G[y]=(G[y]+bb+2*H[y]%mo*b%mo)%mo;H[y]=(H[y]+b)%mo;G[y]=(G[y]+cc+2*H[y]%mo*c%mo)%mo;H[y]=(H[y]+c)%mo;G[y]=(G[y]+dd+2*H[y]%mo*d%mo)%mo;H[y]=(H[y]+d)%mo;G[y]=G[y]*p%mo;H[y]=H[y]*p%mo;ans[y]=(G[y]+2*H[y]%mo*k2[y]%mo+k3[y])%mo;}for (int i=h[x],y;i;i=e[i].nxt){if ((y=e[i].to)==pre) continue;dfs4(y,x);} } int main() {freopen("travel.in","r",stdin);freopen("travel.out","w",stdout);n=read();for (int i=1;i<=n;i++) a[i]=read(),d[i]=read();ll p;for (int i=1,u,v;i<n;i++){u=read();v=read();p=read();link(u,v,p);link(v,u,p);}dfs1(1,0);dfs2(1,0);dfs3(1,0);ans[1]=k3[1];dfs4(1,0);int q=read();while (q--){int s=read();printf("%lld\n",ans[s]);}return 0; }?
?
轉載于:https://www.cnblogs.com/xxzh/p/9818529.html
總結
以上是生活随笔為你收集整理的[JZOJ 5911] [NOIP2018模拟10.18] Travel 解题报告 (期望+树形DP)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用证书登陆Linux服务器
- 下一篇: windows拷贝内容到ubuntu中