ZJOI 2017 樹狀數組(線段樹套線段樹)
阿新 • • 發佈:2019-04-27
需要 const -- long long 題意 原來 eof 二維線段樹 gcd 和不蓋住 \(r\) 兩種情況考慮。設原來相等的概率為 \(p\) ,再進行修改不影響的概率為 \(q\) ,那麽修改後相等的概率就是 \(pq+(1-p)(1-q)\) 。對於第二類詢問也是一樣的,分區間覆蓋 \(l-1\) 點和 \(r\) 點、覆蓋其中一個點、都不覆蓋三種情況考慮。代碼中有切了這一檔分,方便和正解對照。
題意
http://uoj.ac/problem/291
思路
不難發現,九條カレン醬所寫的樹狀數組,在查詢區間 \([1,r]\) 的時候,其實在查詢後綴 \([r,n]\) ;在查詢 \([l,r](l\neq1)\) 的時候,則是在查詢 \([l-1,r-1]\) 。那麽在查詢 \([1,r]\) 的時候,只需要詢問 \(r\) 的前後綴異或是否相等;在查詢 \([l,r](l\neq 1)\) 的時候,只需要詢問 \(a[l-1],a[r]\) 是否相等。
考慮 \(O(n^2)\) 的暴力。我們把詢問分成上述的兩類。第一類詢問如果修改到了點 \(r\) ,則無影響,否則就是相等變不相等的轉化,分詢問區間蓋住 \(r\)
我們可以同時維護住所有答案,然後只接回答詢問。用一個一維數據結構維護每個點 \(x\) 的前綴或者後綴是否相等,一個二維數據結構用來維護 \(a[x],a[y]\) 的值是否相等。修改和上面的暴力是同理的,是對一個區間(一維或二維)的點附上一個修改後相等的概率,對於修改顯然是交換結合都沒什麽關系。那這個一維數據結構選擇線段樹,二維數據結構選擇線段樹套線段樹即可。
二維線段樹比較好寫的寫法是靜點套動點,不過動點套動點也可以寫的。而且這道題其實空間是不夠的,但比較難卡,一般卡不滿。
代碼
#include<bits/stdc++.h> #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;} template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;} typedef long long ll; const int P=998244353; const int N=1e5+5; int op[N],ql[N],qr[N]; int n,m; void exgcd(int a,int b,int &x,int &y) { if(!b){x=1,y=0;return;} exgcd(b,a%b,y,x),y-=a/b*x; } int inv(int a) { int x,y; exgcd(a,P,x,y); return (x%P+P)%P; } namespace Subtask1 { int merge(int x,int y) { return ((1ll*x*y+1ll*(1-x)*(1-y))%P+P)%P; } void Solve() { FOR(i,1,m)if(op[i]==2) { int l=ql[i],r=qr[i]; int p=1; if(l==1) { FOR(j,1,i-1)if(op[j]==1) { int len=qr[j]-ql[j]+1; if(ql[j]<=r&&r<=qr[j]) p=merge(p,inv(len)); else p=merge(p,0); } } else { l--; FOR(j,1,i-1)if(op[j]==1) { int len=qr[j]-ql[j]+1; if(ql[j]<=l&&r<=qr[j]) p=merge(p,1ll*(len-2)*inv(len)%P); else if((ql[j]<=l&&l<=qr[j])||(ql[j]<=r&&r<=qr[j])) p=merge(p,1ll*(len-1)*inv(len)%P); } } printf("%d\n",p); } } }; namespace Subtask2 { int merge(int x,int y) { return ((1ll*x*y+1ll*(1-x)*(1-y))%P+P)%P; } struct SegmentTree { int pw[N<<2]; void build(int k,int l,int r) { pw[k]=1; if(l==r)return; int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); } void update(int k,int L,int R,int val,int l,int r) { if(L<=l&&r<=R) { pw[k]=merge(pw[k],val); return; } int mid=(l+r)>>1; if(L<=mid)update(k<<1,L,R,val,l,mid); if(R>mid)update(k<<1|1,L,R,val,mid+1,r); } int query(int k,int x,int l,int r) { if(l==r)return pw[k]; int mid=(l+r)>>1; if(x<=mid)return merge(pw[k],query(k<<1,x,l,mid)); else return merge(pw[k],query(k<<1|1,x,mid+1,r)); } }; struct SegmentTree2D { int lson[N*450],rson[N*450],pw[N*450]; int rt[N<<2],tot; void build() { memset(rt,0,sizeof(rt)); tot=0; } void create(int &k) { k=++tot; lson[k]=rson[k]=0; pw[k]=1; } void update(int &k,int L,int R,int val,int l,int r) { if(!k)create(k); if(L<=l&&r<=R) { pw[k]=merge(pw[k],val); return; } int mid=(l+r)>>1; if(L<=mid)update(lson[k],L,R,val,l,mid); if(R>mid)update(rson[k],L,R,val,mid+1,r); } int query(int k,int x,int l,int r) { if(!k)return 1; if(l==r)return pw[k]; int mid=(l+r)>>1; if(x<=mid)return merge(pw[k],query(lson[k],x,l,mid)); else return merge(pw[k],query(rson[k],x,mid+1,r)); } void Update(int k,int U,int D,int L,int R,int val,int u,int d,int l,int r) { if(U<=u&&d<=D) { update(rt[k],L,R,val,l,r); return; } int mid=(u+d)>>1; if(U<=mid)Update(k<<1,U,D,L,R,val,u,mid,l,r); if(D>mid)Update(k<<1|1,U,D,L,R,val,mid+1,d,l,r); } int Query(int k,int x,int y,int u,int d,int l,int r) { if(u==d)return query(rt[k],y,l,r); int mid=(u+d)>>1; if(x<=mid)return merge(query(rt[k],y,l,r),Query(k<<1,x,y,u,mid,l,r)); else return merge(query(rt[k],y,l,r),Query(k<<1|1,x,y,mid+1,d,l,r)); } }; SegmentTree ST; SegmentTree2D ST2; void Solve() { ST.build(1,1,n); ST2.build(); FOR(i,1,m) { if(op[i]==1) { int len=qr[i]-ql[i]+1; ST.update(1,ql[i],qr[i],inv(len),1,n); if(ql[i]>1)ST.update(1,1,ql[i]-1,0,1,n); if(qr[i]<n)ST.update(1,qr[i]+1,n,0,1,n); ST2.Update(1,ql[i],qr[i],ql[i],qr[i],1ll*(len-2)*inv(len)%P,1,n,1,n); if(ql[i]>1)ST2.Update(1,1,ql[i]-1,ql[i],qr[i],1ll*(len-1)*inv(len)%P,1,n,1,n); if(qr[i]<n)ST2.Update(1,ql[i],qr[i],qr[i]+1,n,1ll*(len-1)*inv(len)%P,1,n,1,n); } else if(op[i]==2) { if(ql[i]==1)printf("%d\n",ST.query(1,qr[i],1,n)); else printf("%d\n",ST2.Query(1,ql[i]-1,qr[i],1,n,1,n)); } } } }; int main() { scanf("%d%d",&n,&m); FOR(i,1,m)scanf("%d%d%d",&op[i],&ql[i],&qr[i]); if(n<=3000&&m<=3000) { Subtask1::Solve(); return 0; } Subtask2::Solve(); return 0; }
ZJOI 2017 樹狀數組(線段樹套線段樹)