luogu P5445 [APIO2019]路灯

题面传送门
首先用两颗线段树维护每个点所在的亮灯联通块。
然后再建一颗二维线段树,每个点表示到当前为止,有多少个时刻能从 i i i j j j
对于每次修改,依靠两颗线段树维护的区间,来修改。
这里有一个小技巧,在修改时加上 q − t q-t qt,修改时减去,就是答案,还能累加。
查询就查当前点就好了。
代码实现:

#include<cstdio>
using namespace std;
int n,m,k,x,y,z,tot,root[300039],a[300039];
char s;
struct tree{int l,r,f;}f[90000039];
struct set{
	int f[1200039];
	inline void jianshu(int l,int r,int now){
	    if(l==r){f[now]=l;return;}
	    int m=(l+r)>>1;
	    jianshu(l,m,now<<1);jianshu(m+1,r,now<<1|1);
    }
	inline void push(int now){
	    if(f[now]){
	    	f[now<<1]=f[now<<1|1]=f[now];
	    	f[now]=0;
	    }
    }
    inline void get(int x,int y,int z,int l,int r,int now){
	    if(x<=l&&r<=y){f[now]=z;return;}
	    int m=(l+r)>>1;
	    push(now);
	    if(x<=m) get(x,y,z,l,m,now<<1);
	    if(y>m) get(x,y,z,m+1,r,now<<1|1);
    }
    inline int find(int x,int l,int r,int now){
    	if(l==r) return f[now];
    	push(now);
    	int m=(l+r)>>1;
    	if(x<=m) return find(x,l,m,now<<1);
    	else return find(x,m+1,r,now<<1|1);
    }
}fs[2];
inline void gets(int x,int y,int l,int r,int now){
	f[now].f+=y;
	if(l==r) return;
	int m=(l+r)>>1;
	if(x<=m) {
		if(!f[now].l) f[now].l=++tot;
		gets(x,y,l,m,f[now].l);
	}
	else{
		if(!f[now].r) f[now].r=++tot;
		gets(x,y,m+1,r,f[now].r);
	}
}
inline void get(int x,int y,int z){
	if(y>n+1) return;
	while(x<=k){
		if(!root[x]) root[x]=++tot;
		gets(y,z,1,k,root[x]);
		x+=x&-x;
	}
}
inline int finds(int x,int y,int l,int r,int now){
	if(x<=l&&r<=y) return f[now].f;
	int m=(l+r)>>1,fs=0;
	if(x<=m&&f[now].l) fs+=finds(x,y,l,m,f[now].l);
	if(y>m&&f[now].r) fs+=finds(x,y,m+1,r,f[now].r);
	return fs; 
}
inline int find(int x,int y){
	int ans=0;
	while(x){
		if(root[x]) ans+=finds(1,y,1,k,root[x]);
		x-=x&-x;
	}
	return ans;
}
int main(){
//	freopen("12","r",stdin);
//	freopen("1.out","w",stdout);
	register int i;
	scanf("%d%d",&n,&m);k=n+1;
	for(i=1;i<=n;i++){
		s=getchar();
		while(s<'0'||s>'9') s=getchar();
		a[i]=s-'0';
	}
	fs[1].jianshu(1,k,1);fs[0].jianshu(1,k,1);
	for(i=1;i<=n;i++){
		if(a[i]){
			x=fs[0].find(i,1,k,1);y=fs[1].find(i+1,1,k,1);
			get(i+1,y+1,m);get(x,i+1,m);get(x,y+1,-m);get(i+1,i+1,-m);
			fs[0].get(x,y,x,1,k,1);fs[1].get(x,y,y,1,k,1);
			//printf("%d %d %d\n",x,y,tot);
		}
	} 
	for(i=1;i<=m;i++){
		s=getchar();
		while(s<'a'||s>'z') s=getchar();
		if(s=='q'){
			s=getchar();s=getchar();s=getchar();s=getchar();
			scanf("%d%d",&x,&y);
			z=fs[1].find(x,1,k,1);
			if(y>z) printf("%d\n",find(x,y));
			else printf("%d\n",find(x,y)-(m-i));
		}
		else{
			s=getchar();s=getchar();s=getchar();s=getchar();s=getchar();
			scanf("%d",&z);
			a[z]^=1;
			if(a[z]){
			    x=fs[0].find(z,1,k,1);y=fs[1].find(z+1,1,k,1);
			    //printf("%d %d\n",x,y);
			    get(z+1,y+1,m-i);get(x,z+1,m-i);get(x,y+1,-m+i);get(z+1,z+1,-m+i);
			    fs[0].get(x,y,x,1,k,1);fs[1].get(x,y,y,1,k,1);
		    }
		    else{
		    	//if(z==2)printf("%d %d\n",x,y);
		    	x=fs[0].find(z,1,k,1);y=fs[1].find(z,1,k,1);
			    get(z+1,y+1,-m+i);get(x,z+1,-m+i);get(x,y+1,m-i);get(z+1,z+1,m-i);
			    fs[1].get(x,z,z,1,k,1);
				fs[0].get(z+1,y,z+1,1,k,1);
		    }
		}
		//printf("%d %d\n",fs[0].find(2,1,k,1),fs[1].find(2,1,k,1));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值