1. 程式人生 > >BZOJ4785 ZJOI2017樹狀數組(概率+二維線段樹)

BZOJ4785 ZJOI2017樹狀數組(概率+二維線段樹)

cal cor += clas str col else 個數 clu

  可以發現這個寫掛的樹狀數組求的是後綴和。find(r)-find(l-1)在模2意義下實際上查詢的是l-1~r-1的和,而本來要查詢的是l~r的和。也就是說,若結果正確,則a[l-1]=a[r](mod 2)。

  一個很容易想到的思路是線段樹維護每一位為1的概率。然而這其實是不對的,因為每一位是否為1並非獨立事件。

  世界上沒有什麽事情是用一維線段樹解決不了的,如果有,那就兩維

  我們維護每兩位之間相同的概率。考慮一次操作對某兩位的影響。若該次操作包含兩位中的x位,那麽改變兩者間相同狀態的概率就是x/len,len為該次修改的區間長度。設原相同概率為pi,j,那麽操作後概率就變為(1-pi,j

)*x/len+pi,j*(1-x/len)。這個式子神奇的滿足交換律結合律,算的時候我們就不用管順序了。

  用二維線段樹就可以維護了。修改時先拆成外層線段樹上的區間再在內層線段樹修改,查詢時將所有外層區間包含該點的內層線段樹上的操作合在一起。並且需要標記永久化。

  註意l=1時find(l-1)直接返回0而不是整個數組的和,此時詢問的是r後綴和r前綴是否相等,需要特判一下,記錄修改了幾次以及r這一位為0的概率(即和第0位相同的概率)。

  (寫起來出乎意料的短

  (空間需要開的非常大

  (當然也可以cdq分治變成靜態數點掃描線掃過去就好了

#include<iostream> 
#include
<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3
)+(c^48),c=getchar(); return x*f; } #define N 100010 #define P 998244353 int n,m,tot=0,inv[N],L[N<<2],R[N<<2],cnt=0; int root[N<<2]; struct data{int l,r,x; }tree[N*400]; void inc(int &x,int y){x+=y;if (x>=P) x-=P;} int calc(int x,int y){return (1ll*x*(P+1-y)+1ll*y*(P+1-x))%P;} void build(int k,int l,int r) { L[k]=l,R[k]=r; if (l==r) return; int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void modify(int &k,int l,int r,int x,int y,int a) { if (!k) k=++cnt; if (l==x&&r==y) { tree[k].x=calc(tree[k].x,a); return; } int mid=l+r>>1; if (y<=mid) modify(tree[k].l,l,mid,x,y,a); else if (x>mid) modify(tree[k].r,mid+1,r,x,y,a); else modify(tree[k].l,l,mid,x,mid,a),modify(tree[k].r,mid+1,r,mid+1,y,a); } int query(int k,int l,int r,int x,int ans) { if (!k) return ans; ans=calc(ans,tree[k].x); if (l==r) return ans; int mid=l+r>>1; if (x<=mid) return query(tree[k].l,l,mid,x,ans); else return query(tree[k].r,mid+1,r,x,ans); } void change(int k,int l,int r,int x,int y,int a) { if (L[k]==l&&R[k]==r) {modify(root[k],0,n,x,y,a);return;} int mid=L[k]+R[k]>>1; if (r<=mid) change(k<<1,l,r,x,y,a); else if (l>mid) change(k<<1|1,l,r,x,y,a); else change(k<<1,l,mid,x,y,a),change(k<<1|1,mid+1,r,x,y,a); } int getans(int x,int y) { int k=1,s=1; while (1) { s=calc(s,query(root[k],0,n,y,0)); if (L[k]==R[k]) break; if (x<=(L[k]+R[k]>>1)) k<<=1; else k=k<<1|1; } return s%P; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4785.in","r",stdin); freopen("bzoj4785.out","w",stdout); const char LL[]="%I64d"; #else const char LL[]="%lld"; #endif n=read(),m=read(); inv[1]=1; for (int i=2;i<=n;i++) inv[i]=(P-1ll*(P/i)*inv[P%i]%P)%P; build(1,0,n); for (int i=1;i<=m;i++) { int op=read(),l=read(),r=read(); if (op==1) { tot^=1;int x=inv[r-l+1]; change(1,0,l-1,l,r,x); if (r<n) change(1,l,r,r+1,n,x); change(1,l,r,l,r,x*2%P); } else { if (l==1) printf("%d\n",tot?(P+1-getans(l-1,r))%P:getans(l-1,r)); else printf("%d\n",getans(l-1,r)); } } return 0; }

BZOJ4785 ZJOI2017樹狀數組(概率+二維線段樹)