Loading

P5445 [APIO2019] 路灯 题解

题目链接

题目描述

给你一个 01 串,有 \(q\) 个时刻,每个时刻要么把一位取反,要么问你在过去的所有时刻中有多少个时刻 \(a\)\(b-1\) 之间都为 1。

题目分析

观察题目,我们会发现可以把全为 1 的段看做一个连通块,如果两个位置在一个块内则可以互相到达,修改某个位置的值就相当于把两边的连通块合并或者分裂。

但是我们此时并非维护一个动态的连通块,而是需要知道所有时刻的信息,但是如果又把所有时刻遍历一遍会超时,考虑能不能用空间换时间,储存下所有时刻的信息方便维护。

容易观察到,维护一个连通块的目的无非是为了检查某两个点联不联通,那么我们可以抛弃连通块,转而维护两个点联通的时间数,这看似有些暴力,毕竟从空间复杂度上看 \(n^2\) 规模就已经超标了,别急,让我们先看看题目怎么操作。

对于一个询问操作自然没什么好说的,那么对于修改操作则如先前所述是将两边连通块分裂或者合并,我们把操作更改一下,记 \(l_1\)\(r_1\)\(l_2\)\(r_2\) 分别为两边连通块的左右端点,则合并操作表示所有左端点在 \([l_1,r_1]\) 内,右端点在 \([l_2,r_2]\) 的点对以后都联通,分裂相反。

注意到受影响的点实际上在平面内构成一个矩形,而询问相当于单点求值,我们能不能把修改转化成对于矩形的修改呢?当然可以,我们把对于一个点实际有效的时间段抽出来看,它实际上可以差分成一次单点加和单点减。

记当前时刻为 \(t\),只要合并时把整个矩形加上 \(q-t\),分裂时减去 \(q-t\) 即可,差分后可以使用 cdq 分治或者树套树解决,另外要注意的是查询时如果还联通,由于不考虑以后的时间,要将答案减去 \(q-t\)

那么左右联通块如何维护呢,其实很简单,要么模仿珂朵莉树用 set 维护,要么用一颗线段树维护查询时二分即可,笔者这里使用了线段树的写法,不过细节多而且复杂度较高,还是建议使用 set 维护。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<cstring>
#define ite set<pai>::iterator
#define N 300005
using namespace std;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
int tot,num,sta[N];
int a[N],rt[N];
int n,q;
struct Node{
	int ls,rs,sum;
	#define ls(x) tr[x].ls
	#define rs(x) tr[x].rs
	#define s(x) tr[x].sum
}tr[N<<8];
int query(int x,int l,int r,int L,int R){
	if(!x) return 0;
	if(L>=l && R<=r)return s(x);
	int mid=(L+R)>>1,ans=0;
	if(l<=mid) ans+=query(ls(x),l,r,L,mid);
	if(r>mid) ans+=query(rs(x),l,r,mid+1,R);
	return ans;
}
void change(int &x,int p,int L,int R,int s){
	if(!x) x=++tot;
	s(x)+=s;if(L==R) return;
	int mid=(L+R)>>1;
	if(p<=mid) change(ls(x),p,L,mid,s);
	else change(rs(x),p,mid+1,R,s);
}
int lowbit(int x){
	return x&(-x);
}
int ask(int x,int y){
	int ans=0;
	while(x){
		ans+=query(rt[x],1,y,1,n+2);
		x-=lowbit(x);
	}
	return ans;
}
void add(int x,int y,int p){
	while(x<=n){
		change(rt[x],y,1,n+2,p);
		x+=lowbit(x);
	}
}
struct no{
	int ls,rs,sum,l,r;
	#define tls(x) t2[x].ls
	#define trs(x) t2[x].rs
	#define ts(x) t2[x].sum
	#define tl(x) t2[x].l
	#define tr(x) t2[x].r
}t2[N<<2];
void build(int &x,int l,int r){
	x=++num;tl(x)=l;tr(x)=r;
	if(l==r){ts(x)=sta[l];return;}
	int mid=(l+r)>>1;
	build(tls(x),l,mid);build(trs(x),mid+1,r);
	ts(x)=ts(tls(x))+ts(trs(x));
}
void ct(int x,int p){
	if(tl(x)==tr(x)) {ts(x)=sta[tl(x)];return;}
	int mid=(tl(x)+tr(x))>>1;
	if(p<=mid) ct(tls(x),p);
	else ct(trs(x),p);
	ts(x)=ts(tls(x))+ts(trs(x));
}
int qt(int x,int l,int r){
	if(tl(x)>=l && tr(x)<=r) return ts(x);
	int mid=(tl(x)+tr(x))>>1;int ans=0;
	if(l<=mid) ans+=qt(tls(x),l,r);
	if(r>mid) ans+=qt(trs(x),l,r);
	return ans;
}
int findr(int x){
	if(x==n) return n+1;
	if(qt(1,x+1,x+1)==0) return x+1;
	int l=x+1,r=n;
	while(l<r){
		
		int mid=(l+r+1)>>1;
		if(qt(1,x+1,mid)==mid-x){
			l=mid;
		}
		else r=mid-1;
	}
	return l+1;
}
int findl(int x){
	if(x==1) return 1;
	if(qt(1,x-1,x-1)==0){
		return x;
	}
	int l=1,r=x-1;
	while(l<r){
		int mid=(l+r)>>1;
		if(qt(1,mid,x-1)==x-1-mid+1) r=mid;
		else l=mid+1;
	}
	return l;
}
void opti(int l,int r,int ll,int rr,int p){
	add(l,r,p);add(ll+1,r,-p);add(l,rr+1,-p);add(ll+1,rr+1,p);
}
int main(){
	n=read();q=read();int l,r;char ch;string s;
	for(int i=1;i<=n;i++){cin>>ch;sta[i]=ch-'0';}
	build(l,1,n);int st=0;
	for(int i=1;i<=n;i++){
		if(sta[i]==1 && sta[i-1]==0) st=i;
		if(sta[i]==1 && sta[i+1]==0){
			opti(st,st,i+1,i+1,q);
		}
	}
	for(int i=1;i<=q;i++){
		cin>>s;
		if(s=="query"){
			l=read();r=read();if(l==r){cout<<i<<endl;continue;}
			int ans=ask(l,r);
			if(qt(1,l,r-1)==(r-1-l+1)) ans+=i-q;
			cout<<ans<<endl;
		}
		else{
			l=read();
			int ll=findl(l);
			int rr=findr(l);
			if(sta[l]==0)opti(ll,l+1,l,rr,q-i);
			else opti(ll,l+1,l,rr,i-q);
			sta[l]^=1;ct(1,l);
			
		}
	}
}

posted @ 2023-03-19 14:38  eastcloud  阅读(35)  评论(0编辑  收藏  举报