[APIO2019]路灯

XXXIII.[APIO2019]路灯

实际上本来是在刷CDQ分治的题来着的,但是CDQ分治是众所周知地抽象,所以在碰到三维数点问题时,除非卡空间,否则一律请选择树套树……

我们可以用 set 来维护连通性。显然,若 \([l,r]\) 这一段的路灯全亮,则所有 \(a,b\in[l,r+1]\) 都是合法的。

考虑差分。当一段 \([l,r]\) 初次联通,就矩形加 \(T-t\),其中 \(T\) 是总时间,\(t\) 是当前时间;当其初次不连通,就矩形减 \(T-t\)

维护联通与否可以使用珂朵莉树,或者说,setpair

明显,这是带时间轴矩形加问题,是三维数点问题,鉴于CDQ太难写,我选择BIT套权值线段树。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,rt[300100],cnt;
#define mid ((l+r)>>1)
struct node{int lson,rson,tag;}seg[20010000];
void modify(int &x,int l,int r,int L,int R,int val){
	if(l>R||r<L)return;
	if(!x)x=++cnt;
	if(L<=l&&r<=R)seg[x].tag+=val;
	else modify(seg[x].lson,l,mid,L,R,val),modify(seg[x].rson,mid+1,r,L,R,val);
}
int query(int x,int l,int r,int P){
	if(l>P||r<P||!x)return 0;
	return seg[x].tag+query(seg[x].lson,l,mid,P)+query(seg[x].rson,mid+1,r,P);
}
void ADD(int x,int L,int R,int val){while(x<=n)modify(rt[x],1,n,L,R,val),x+=x&-x;}
int SUM(int x,int P){int ret=0;while(x)ret+=query(rt[x],1,n,P),x-=x&-x;return ret;}
void ADD(int l,int r,int val){ADD(l,l,r,val),ADD(r+1,l,r,-val);}
set<pair<int,int> >s;
char str[300100],rts[10];
int main(){
	scanf("%d%d",&n,&m),scanf("%s",str+1);
	for(int i=1,j=1;i<=n;i=j){
		while(j<=n&&str[i]==str[j])j++;
		if(str[i]=='1')s.insert(make_pair(i,j-1)),ADD(i,j-1,m);
	}
	for(int i=1,a,b;i<=m;i++){
		scanf("%s%d",rts,&a);
		if(rts[0]=='t'){
			if(str[a]=='1'){
				auto it=--s.upper_bound(make_pair(a,0x3f3f3f3f));
				int L=it->first,R=it->second;s.erase(it);
				ADD(L,R,i-m);
				if(L!=a)ADD(L,a-1,m-i),s.insert(make_pair(L,a-1));
				if(R!=a)ADD(a+1,R,m-i),s.insert(make_pair(a+1,R));
			}else{
				auto it=s.lower_bound(make_pair(a,0x3f3f3f3f));
				int L=a,R=a;
				if(it!=s.end()&&it->first==a+1)ADD(it->first,it->second,i-m),R=it->second,it=s.erase(it);
				if(it!=s.begin()&&(--it)->second==a-1)ADD(it->first,it->second,i-m),L=it->first,s.erase(it);
				ADD(L,R,m-i),s.insert(make_pair(L,R)); 
			}
//			for(auto i:s)printf("(%d %d)",i.first,i.second);puts("");
			str[a]^=1;
//			printf("%s\n",str+1);
		}else{
			scanf("%d",&b),b--;
			int ret=SUM(a,b);
			auto it=s.upper_bound(make_pair(a,0x3f3f3f3f));
			if(it!=s.begin()){
				--it;
				if(it->first<=a&&it->second>=b)ret+=i-m;
			}
			printf("%d\n",ret);
		}
	}
	return 0;
}

posted @ 2021-04-02 17:09  Troverld  阅读(92)  评论(0编辑  收藏  举报