P3688 [ZJOI2017]树状数组

先讲一句废话,因为有mod 2,所以这题的计算只有异或。

题解告诉我们,这个写错的树状数组的作用是单点修改,询问后缀异或和。

至于证明的话,作为一名蒟蒻,我选择打表冷静一下。

感性理解后,发现就是对的。然后,

题解又说了,这样就能轻易想到动态二维数点。(讲道理打死我都想不到)

将每个修改看成一个点(L,R),询问看成一个矩形x≤r,y≥r(l=1)或两个矩形x≤r,y≥r和x≤l-1,y≥l-1

(l>1)。其中,两个矩形有重叠让人很不爽,所以改成三个矩形x≤l,l≤y<r(k=1)和l<x≤r,y≥r(k=1)和x≤l,y≥r(k=2)。

然后发现还是不会。

听说有种好姿势叫cdq分治,核心是分治时要考虑前半段对后半段的影响。

这样讲了等于没讲,那就直接以这题为例。

对询问的顺序进行分治,先处理好前半段,再取出后半段所有询问和前半段所有修改,进行静态二维数点,结果由全局变量保存。

代码思路:

init:
pii pos[N];
struct REC{int o,L,R,D,U,w;}rec[N];
int wzp[N],wzr[N],lnp,lnr,ans[N];//wz表示最右能到达的位置 
if(x==1){
	pos[++lnp]= mkp(y,z);
}
else if(y==1){
	rec[++lnr]={1,z,z,n};
}
else{
	rec[++lnr]={i,1,z,y,z-1,1};
	rec[++lnr]={i,y+1,z,z,n,1}; 
	rec[++lnr]={i,1,y,z,n,2};
}
cdq(1,n);
int cdq(int le,int ri){
	if(le==ri){
		return;
	}
	int mid=le+ri>>1;
	cdq(le,mid),cdq(mid+1,r);
	solve(wzp[le],wzp[ri],wzr[le-1]+1,wzr[ri]);
}
struct VEC{
	int o,L,R;//表示点时o为0,表示矩形是为下标,正负表示该加还是减。 
}vec[N];int cnt;
bool operator <(VEC x,VEC y){
	if(x.L!=y.L)return x.L<y.L;
	if(x.R!=y.R)return x.R<y.R;
	return !x.o;
}
void solve(lp,rp,lr,rr){
	rep(i,lp,rp){
		vec[++cnt]=(VEC){0,pos[i].X,pos[i].Y};
	}
	rep(i,lp,rp){
		vec[++cnt]=(VEC){i,rec[i].R,rec[i].U};
		vec[++cnt]=(VEC){-i,rec[i].R,rec[i].D-1};
		vec[++cnt]=(VEC){-i,rec[i].L-1,rec[i].U};
		vec[++cnt]=(VEC){i,rec[i].L-1,rec[i].D-1};
	}
	sort();
	rep(i,1,cnt){
		if(!o)addt();
		else{
			query();
			if(o>0)adda();
			else suba();
		}
	}
}

为什么add和sub没有写呢?

因为我发现写sub的时候p=(p'+q)/(1-2q),q=1/2时无意义!

所以该怎么办呢?蒟蒻想了很久也没能解决问题,最终选择向黑恶势力低头,改用二维线段树!

虽然二维线段树很烧内存,但我还是想赌一把,希望通过卡常把这题A掉。(讲道理现在不是在考场上,多爆几发OJ没有太大问题)


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值