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;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

posted @ 2017-04-09 23:00  ljh_2000  阅读(1139)  评论(0编辑  收藏  举报