BZOJ4785 [Zjoi2017]树状数组
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
题目链接:BZOJ4785
正解:二维线段树
解题报告:
考场上我一眼看出了这个变成了后缀和,然而被$l=1$坑成了暴力分== 好坑啊…
考虑变成后缀和之后,我们只需要$check$一下$l-1$和$r$的被修改次数是否相同,这个概率如果我们能维护的话每次直接查询就好了。
我们把$(x,y)$看成一个二维数点,表示$x$与$y$修改次数相同的概率,那么不相同的概率也能直接算了。
为了方便,我们只记录$x<y$的情况就好了。这个东西我们显然可以用二维线段树来维护。
考虑每次查询直接查询$(l-1,r)$就好了。
对于修改呢,对于$[1,l-1]$、$[l+1,r]$这两个区间都会对$[l,r]$中的数有$1-p$的概率不改变相同性,$p=1/区间长度$,即选中区间一个数的概率。
而考虑区间内部的情况呢,就是$1-p*2$。
这个东西直接在二维线段树上维护就好了,这个标记可以直接合并,因为两个概率可以用$p1*p2+(1-p1)*(1-p2)$的式子合并。
注意卡卡空间,卡卡常数==
//It is made by ljh2000 //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。 #include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <string> #include <queue> #include <cmath> #include <ctime> #define lc root<<1 #define rc root<<1|1 #define rep(i,j,k) for(int i=j;i<=k;i++) #define reg(i,x) for(int i=first[x];i;i=next[i]) using namespace std; typedef long long LL; const int MAXN = 200011; const int mod = 998244353; int n,m,cnt,RT[MAXN*3]; int ans,P[MAXN*180]; struct node{ int ls,rs; }a[MAXN*180];//维护二元组修改次数相等的概率 inline int fast_pow(int x,int y){ int r=1; while(y>0) { if(y&1) r=1LL*r*x%mod; x=1LL*x*x%mod; y>>=1; } return r; } inline int merge(int x,int y){ return ( 1LL*x*y%mod+1LL*(1-x+mod)*(1-y+mod)%mod )%mod; } inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } inline void build(int root,int l,int r){ cnt=max(cnt,root); if(l==r) return ; int mid=(l+r)>>1; build(lc,l,mid); build(rc,mid+1,r); } inline void modify(int &root,int l,int r,int ql,int qr,int CC){ if(!root) root=++cnt,P[root]=1;//!!!默认修改次数相等 if(ql<=l && r<=qr) { P[root]=merge(P[root],CC); return ; } int mid=(l+r)>>1; if(ql<=mid) modify(a[root].ls,l,mid,ql,qr,CC); if(qr>mid) modify(a[root].rs,mid+1,r,ql,qr,CC); } inline void Modify(int root,int l,int r,int ql,int qr,int down,int up,int CC){ if(ql<=l && r<=qr) { modify(RT[root],0,n+1,down,up,CC); return ; } int mid=(l+r)>>1; if(ql<=mid) Modify(lc,l,mid,ql,qr,down,up,CC); if(qr>mid) Modify(rc,mid+1,r,ql,qr,down,up,CC); } inline void query(int &root,int l,int r,int pos){ if(!root) return ; ans=merge(ans,P[root]);//!!! if(l==r) return ; int mid=(l+r)>>1; if(pos<=mid) query(a[root].ls,l,mid,pos); else query(a[root].rs,mid+1,r,pos); } inline void Query(int root,int l,int r,int posx,int posy){ if(RT[root]) query(RT[root],0,n+1,posy);//!!! if(l==r) return ; int mid=(l+r)>>1; if(posx<=mid) Query(lc,l,mid,posx,posy); else Query(rc,mid+1,r,posx,posy); } inline void work(){ n=getint(); m=getint(); int type,l,r,p; build(1,0,n); for(int i=1;i<=m;i++) { type=getint(); l=getint(); r=getint(); if(type==1) { p=fast_pow(r-l+1,mod-2); //[l,r]->[1,l-1] 1-p if(l>1) { Modify(1,1,n,1,l-1,l,r,(1-p+mod)%mod); modify(RT[0],1,n,1,l-1,0); } //[l,r]->[r+1,n] 1-p if(r<n) { Modify(1,1,n,l,r,r+1,n,(1-p+mod)%mod); modify(RT[0],1,n,r+1,n,0);//!!!n+1,后缀和!!! } //[l,r]->[l,r] 1-p*2 if(l!=r) Modify(1,1,n,l,r,l,r,(1-p*2+mod+mod)%mod); //l=1说明查询前缀和和后缀和是否相等 //那么修改序列中每个数的前缀和与后缀和相等概率 modify(RT[0],1,n,l,r,p);//只有修改到自己才不会改变前缀和与后缀和的相等性 } else { ans=1; if(l==1) query(RT[0],1,n,r); else Query(1,1,n,l-1,r); printf("%d\n",ans); } } } int main() { #ifndef ONLINE_JUDGE freopen("bit.in","r",stdin); freopen("bit.out","w",stdout); #endif work(); return 0; } //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!