$[ Luogu 3924 ] $康納的線段樹
阿新 • • 發佈:2018-10-13
solution digi def 根據 class ++ 子節點 \n show
,那麽它被選在鏈裏的概率就是\(\frac 1{2^k}\) ,所以一個葉子節點 \(i\) 從自己開始所有祖先的概率和為
\[ \sum_{i=0}^{deep[i]} \frac 1{2^i}=2-\frac 1{2^i} \]
因為期望的線性性,所以這一個葉節點增加,所有祖先會對總期望加上選擇概率\(\times\)增量的答案,不妨直接記錄每個葉節點及其祖先節點在這個節點增加時的概率和,這樣就可以直接統計一個節點對答案的增量了。 ,而且用 \(printf\) 輸出 \(\%lf\) 是非常慢的,所以要先強轉\(long\ long\),然後註意轉換的時候要設\(eps\)。
\(\\\)
\(Description\)
現在有一個線段樹維護長為\(N\)的數列,實現方式是\(mid=((l+r)>>1)\),支持區間加,節點維護區間和。
共有\(M\)次區間加,每次區間加之後詢問,選一條從根到任意葉子節點的鏈,其上的節點維護的區間和之和的期望。
- \(N,M\le 10^6\)
\(\\\)
\(Solution\)
根據期望的線性性,開始的想法是維護每一個節點在選中的鏈上的概率,每次修改把修改乘上概率,復雜度\(\text O(NlogN)\),卡\(T\)三個點。
然後仔細回想每一次增加的過程。對一個葉子增加,顯然對他的所有祖先都會有貢獻,假如一個節點深度為\(k\)
\[ \sum_{i=0}^{deep[i]} \frac 1{2^i}=2-\frac 1{2^i} \]
因為期望的線性性,所以這一個葉節點增加,所有祖先會對總期望加上選擇概率\(\times\)增量的答案,不妨直接記錄每個葉節點及其祖先節點在這個節點增加時的概率和,這樣就可以直接統計一個節點對答案的增量了。
然後區間加的時候區間的增量都相同,所以把概率求一個前綴和,然後總增量就是區間概率和\(\times\)單點增量了。
這題邪在需要\(long\ douvble\)
\(\\\)
\(Code\)
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 1000010 #define R register #define gc getchar #define mid ((l+r)>>1) using namespace std; typedef long long ll; typedef long double ld; inline ll rd(){ ll x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } ll n,m; ld ans,bin[30],p[N]; const ld eps=1e-9; void dfs(ll dp,ll l,ll r){ if(l==r){p[l]=dp;return;} dfs(dp+1,l,mid); dfs(dp+1,mid+1,r); } int main(){ n=rd(); m=rd(); bin[0]=rd(); dfs(0,1,n); for(R ll i=1;i<=30;++i) bin[i]=bin[i-1]/2; for(R ll i=1;i<=n;++i){ p[i]=(bin[0]*2)-bin[(ll)p[i]]; ans+=(ld)rd()*p[i]; p[i]+=p[i-1]; } for(R ll i=1,l,r,w;i<=m;++i){ l=rd(); r=rd(); w=rd(); ans+=(ld)w*p[r]-(ld)w*p[l-1]; printf("%lld\n",(ll)floor(ans+eps)); } return 0; }
$[\ Luogu\ 3924\ ]\ $康納的線段樹