zoukankan      html  css  js  c++  java
  • [ZJOI2017]树状数组

    神仙题

    就贴一个(loj)的链接吧,因为也就只有(loj)能过

    发现可怜的树状数组写反了,那么根据经验可怜求得其实就是后缀和

    发现都是在二进制下进行的操作,于是那个减法和加法没有什么区别,我们可以看成是后缀(l-1)的和减掉后缀(r)的和

    就是

    [sum_{i=l-1}^na_i-sum_{i=r}^na_i ]

    发现求得就是([l-1,r-1])这段区间的和

    我们考虑一下这一段区间和([l,r])的差别

    发现([l-1,r-1]-a_{l-1}+a_r=[l,r])

    所以如果(a_r=a_{l-1}),我们就会发现这两段区间区间和是相等的

    现在我们的问题就变成了求(a_{l-1})(a_r)相等的概率了

    (dp_{i,0/1})表示(i)位置是(0/1)的概率

    显然对于(iin[l,r]),有这样的转移

    [dp_{i,0}=frac{1}{r-l+1}dp'_{i,1}+frac{r-l}{r-l+1}dp'_{i,0} ]

    [dp_{i,1}=frac{1}{r-l+1}dp'_{i,0}+frac{r-l}{r-l+1}dp'_{i,1} ]

    发现我们可以把转移写成矩阵的形式

    [egin{bmatrix}dp'_{i,0}\dp'_{i,1}end{bmatrix} imes egin{bmatrix}frac{r-l}{r-l+1} frac{1}{r-l+1}\frac{1}{r-l+1} frac{r-l}{r-l+1}end{bmatrix}=egin{bmatrix}dp_{i,0}\dp_{i,1}end{bmatrix} ]

    这样我们就可以使用数据结构来维护矩阵乘法了

    这样这道题就做完了吗?

    显然不是

    发现如果有两个位置(i,jin[l,r]),我们会把这两个位置都修改一遍,如果再查询这两个位置是否相等的话,我们就相当于在([l,r])这个区间选择了两个数加(1)

    这一点也不科学啊

    我们考虑一段区间同时包含了我们当前的两个询问点(i,j)

    (frac{2}{r-l+1})的概率使得其中一个被加(1)(frac{r-l-1}{r-l+1})的概率加到了某一个没有什么影响的数上面去了

    如果一个数加(1),那么我们的判断条件就从两个数相同变成了两个数不同,也就是要求发生改变

    于是我们再来一个(f_{0/1})表示当前的要求是两个数不同/相同的概率

    这里也有一个矩阵

    [egin{bmatrix}f'_{0}\f'_{1}end{bmatrix} imes egin{bmatrix}frac{r-l-1}{r-l+1} frac{2}{r-l+1}\frac{2}{r-l+1} frac{r-l-1}{r-l+1}end{bmatrix}=egin{bmatrix}f_{0}\f_{1}end{bmatrix} ]

    对于(i,j)询问的答案就应该是

    [f_0(dp_{i,0} imes dp_{j,1}+dp_{i,1} imes dp_{j,0})+f_1(dp_{i,0} imes dp_{j,0}+dp_{i,1} imes dp_{j,1}) ]

    于是我们需要处理出所有的包含(i)不包含(j),包含(j)不包含(i)的区间的矩阵乘积,求出(dp_{i,0},dp_{i,1},dp_{j,0},dp_{j,1})

    以及所有包含(i)又包含(j)的区间的矩阵乘积,求出(f_0,f_1)

    发现这是一个二维的关系,我们可以直接上树套树

    看起来就解决了

    但是还是没有完,我们认真读题会发现在(l-1=0)的时候给出的代码特判退出了

    那么我们就没有办法求(a_{l-1}=a_r)的概率了,发现这个时候可怜求得其实是(r)的后缀和

    现在我们的问题变成了判断(r)的前缀和等于后缀和的概率

    注意到这个前缀和后缀有一个公共点是(r),也就是(r)被修改是没有什么影响的

    我们来一个树状数组,求出([1,r-1])([r+1,n])这两段的和

    求得就是有多少段区间被([1,r-1])([r+1,n])完全包含,这些区间无论如何随机都一定能让这段区间的和加(1)

    之后对于那些跨过(r)的区间,我们有(frac{1}{len})的概率选中(r)这个位置使得没什么影响,(frac{len-1}{len})的概率选在前缀和后缀中

    我们还是可以写成矩阵来进行转移,于是还是需要一个树套树来维护

    现在终于做完了,但还是二维线段树的空间太大了实在开不过去了

    仅仅能在(loj)上过的代码

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    const int maxn=1e5+5;
    const int M=2e7+10;
    const LL mod=998244353;
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    void exgcd(LL a,LL b,LL &x,LL &y) {if(!b) {x=1,y=0;return;}exgcd(b,a%b,y,x);y-=a/b*x;}
    inline LL getInv(LL a) {LL x,y;exgcd(a,mod,x,y);return (x%mod+mod)%mod;}
    struct mat{int a[2][2];};
    inline mat operator*(mat a,mat b) {
    	mat c;
    	c.a[0][0]=((LL)a.a[0][0]*(LL)b.a[0][0]%mod+(LL)a.a[0][1]*(LL)b.a[1][0]%mod)%mod;
    	c.a[0][1]=((LL)a.a[0][0]*(LL)b.a[0][1]%mod+(LL)a.a[0][1]*(LL)b.a[1][1]%mod)%mod;
    	c.a[1][0]=((LL)a.a[1][0]*(LL)b.a[0][0]%mod+(LL)a.a[1][1]*(LL)b.a[1][0]%mod)%mod;
    	c.a[1][1]=((LL)a.a[1][0]*(LL)b.a[0][1]%mod+(LL)a.a[1][1]*(LL)b.a[1][1]%mod)%mod;
    	return c;
    }
    int n,m,cnt;
    LL dp[2][2],f[2],g[2];
    int ls[maxn<<2],rs[maxn<<2],pos[maxn];
    void build(int x,int y,int i) {
    	ls[i]=x,rs[i]=y;
    	if(x==y) {pos[x]=i;return;}
    	int mid=x+y>>1;
    	build(x,mid,i<<1);build(mid+1,y,i<<1|1);
    }
    int l[M],r[M];mat d[M];
    int add(int now,int pos,int x,int y,mat v) {
    	if(!now) {
    		now=++cnt;
    		d[now].a[0][0]=d[now].a[1][1]=1;
    	}
    	d[now]=d[now]*v;
    	if(x==y) return now;
    	int mid=x+y>>1;
    	if(pos<=mid) l[now]=add(l[now],pos,x,mid,v);
    		else r[now]=add(r[now],pos,mid+1,y,v);
    	return now;
    }
    mat ask(int now,int x,int y,int lx,int ry) {
    	if(!now) return d[0];
    	if(x<=lx&&y>=ry) return d[now];
    	int mid=lx+ry>>1;
    	if(y<=mid) return ask(l[now],x,y,lx,mid);
    	if(x>mid) return ask(r[now],x,y,mid+1,ry);
    	return ask(l[now],x,y,lx,mid)*ask(r[now],x,y,mid+1,ry);
    }
    struct Segment_Tree {
    	int rt[maxn*3];
    	void change(int x,int y,mat v) {
    		int i=pos[x];
    		while(i) 
    			rt[i]=add(rt[i],y,1,n,v),i>>=1;
    	}
    	mat query(int x,int y,int lx,int ry,int i) {
    		if(x<=ls[i]&&y>=rs[i]) return ask(rt[i],lx,ry,1,n);
    		int mid=ls[i]+rs[i]>>1;
    		if(y<=mid) return query(x,y,lx,ry,i<<1);
    		if(x>mid) return query(x,y,lx,ry,i<<1|1);
    		return query(x,y,lx,ry,i<<1)*query(x,y,lx,ry,i<<1|1);
    	}
    }A,B,K;
    struct Bit {
    	int c[maxn];
    	#define lb(x) (x&-x)
    	inline void add(int x) {
    		for(re int i=x;i<=n;i+=lb(i)) c[i]^=1;
    	}
    	inline int ask(int x) {
    		int now=0;
    		for(re int i=x;i;i-=lb(i)) now^=c[i];
    		return now;
    	} 
    }C,D;
    int main() {
    	n=read(),m=read();
    	build(1,n,1);
    	d[0].a[0][0]=1;
    	d[0].a[1][1]=1;
    	int opt,x,y;
    	int h=0;
    	while(m--) {
    		opt=read(),x=read(),y=read();
    		if(opt==1) {
    			mat v;h^=1;
    			C.add(x),D.add(y);
    			LL len=y-x+1;
    			LL inv=getInv(len);
    			v.a[1][1]=v.a[0][0]=(len-1)*inv%mod;
    			v.a[0][1]=v.a[1][0]=inv;
    			A.change(x,y,v);
    			std::swap(v.a[0][0],v.a[0][1]);
    			std::swap(v.a[1][0],v.a[1][1]);
    			K.change(x,y,v);
    			if(x==y) continue;
    			v.a[1][1]=v.a[0][0]=(len-2)*inv%mod;
    			v.a[1][0]=v.a[0][1]=2ll*inv%mod;
    			B.change(x,y,v);
    		}
    		if(opt==2) {
    			x--;
    			mat p1,p2,p3;
    			if(!x) {
    				p1=K.query(1,y,y,n,1);
    				int o=D.ask(y-1),t=(h^C.ask(y));
    				if(o==t) printf("%d
    ",p1.a[1][1]);
    					else printf("%d
    ",p1.a[0][1]);
    				continue;
    			}
    			p1=A.query(1,x,x,y-1,1);
    			p2=A.query(x+1,y,y,n,1);
    			p3=B.query(1,x,y,n,1);
    			dp[0][0]=p1.a[0][1],dp[0][1]=p1.a[1][1];
    			dp[1][0]=p2.a[0][1],dp[1][1]=p2.a[1][1];
    			f[0]=p3.a[0][1],f[1]=p3.a[1][1];
    			g[0]=dp[0][0]*dp[1][0]%mod+dp[0][1]*dp[1][1]%mod;
    			g[0]%=mod;
    			g[1]=dp[0][1]*dp[1][0]%mod+dp[0][0]*dp[1][1]%mod;
    			g[1]%=mod;
    			printf("%lld
    ",(f[0]*g[1]%mod+f[1]*g[0]%mod)%mod);
    		}
    	}
    	return 0;
    }
    

    upd

    发现正解好简单,我真是一个思博,活该被卡

  • 相关阅读:
    原生js写的flybird小游戏
    vue的图片上传
    移动端常用的meta标签,媒体查询以及一些样式设置《转载收藏》
    面向对象写的简单的colors rain
    canvas小球
    JS基础(常见操作函数和数组的方法)
    JS基础(instanceof详解)
    JS基础(垃圾回收)
    JS基础(arguments详解)
    JS基础(显性原型和隐性原型)
  • 原文地址:https://www.cnblogs.com/asuldb/p/10619936.html
Copyright © 2011-2022 走看看