F. Raging Thunder 线段树 + 区间合并
先感叹一句,这个题目真变态,我写了五六个小时。。。。。而且还是在知道怎么写的情况下。。。
题目大意:
给你一个字符串,这个字符串只包含 \(>\) 和 \(<\) ,其中 \(>\) 表示该位置的球会向右滚一格, \(<\) 表示该位置的球会向左滚一格,如果最左边是 \(<\) 则表示球会落到位置为0的这个洞里,如果最右边是 \(>\) 则表示球会落到位置为 \(n+1\) 的这个洞里。一共有 \(m\) 次询问,每次询问给你一个区间 \([l,r]\) ,表示这个区间的所有的字符会反向,即 \(>\) 变成 \(<\) ,\(<\) 变成 \(>\) ,而且这个区间每个位置会落下一颗球,找出一个洞中最大的球的数量,提示:每一次询问之后,字符的改变会保留。
题解:
- 先把这个字符串转化成01字符串,\(>\) 表示1,\(<\) 表示0,这个题目就转化成求最长的1...0...字符串
- 然后你会发现我们要求的字符串就类似于 00111000111,起始是01,结束也是01,结果转化为求间隔最远的01。
- 第三步就是把01,00,10,11进行转化以便于求间隔最远的01
- 把00转化为2,01转化为0,10转化为3,11转化为1
- 最后需要维护的有最长的0的间隔,最长的3的间隔,最左边的0、3,最右边的0、3
- 对于一个区间进行更新,容易得到就是把0换成了3,1换成了2
- 例如:000111 那么转化后 22011,对这整个区间进行更新则变成111000,转化后11322
- 但是要注意的是更新一个区间,其左右端点需要单独更新,这个可以自己想清楚
这个是我写之前就知道了的,之后写的过程中写出来很多的bug
- 求解需要一个区间合并
- 区间合并的时候要注意限制在求的范围内
- 还有一些写在代码里面了
这个题目的转化挺难想的,也很考验码力,和区间合并的线段树很像
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
#define debug(x) printf("debug:%s=%d\n",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn =5e5+10;
int lc0[maxn*4],lc3[maxn*4],rc0[maxn*4],rc3[maxn*4];
int len0[maxn*4],len3[maxn*4],a[maxn],lazy[maxn*4];
char s[maxn];
void push_up(int id){
lc0[id]=min(lc0[id<<1],lc0[id<<1|1]);
lc3[id]=min(lc3[id<<1],lc3[id<<1|1]);
rc0[id]=max(rc0[id<<1],rc0[id<<1|1]);
rc3[id]=max(rc3[id<<1],rc3[id<<1|1]);
len0[id]=max(len0[id<<1],len0[id<<1|1]);
len3[id]=max(len3[id<<1],len3[id<<1|1]);
if(lc0[id<<1|1]!=inf&&rc0[id<<1]!=0) len0[id]=max(len0[id],lc0[id<<1|1]-rc0[id<<1]);
if(lc3[id<<1|1]!=inf&&rc3[id<<1]!=0) len3[id]=max(len3[id],lc3[id<<1|1]-rc3[id<<1]);
// printf("lec0[%d]=%d\n",id,len0[id]);
// printf("l len=%d r len=%d\n",len0[id<<1],len0[id<<1|1]);
// printf("lc0[%d]=%d rc[%d]=%d\n",id<<1|1,lc0[id<<1|1],id<<1,rc0[id<<1]);
}
void build(int id,int l,int r){
if(l==r){
if(a[l]==0) lc0[id]=l,rc0[id]=l;
if(a[l]==3) lc3[id]=l,rc3[id]=l;
return ;
}
int mid=(l+r)>>1;
build(id<<1,l,mid);
build(id<<1|1,mid+1,r);
push_up(id);
}
void change(int id,int l,int r){
//如果是叶子节点,则直接更新即可,不需要进行swap
if(l==r) {
a[l]=3-a[l];
//无论这个位置是什么都要注意状态的更新,是0,则也要更新3的状态
if(a[l]==0) lc0[id]=l,rc0[id]=l,lc3[id]=inf,rc3[id]=0;
else if(a[l]==3) lc3[id]=l,rc3[id]=l,lc0[id]=inf,rc0[id]=0;
else lc0[id]=inf,lc3[id]=inf,rc0[id]=0,rc3[id]=0;
// printf("a[%d]=%d id=%d l=%d r=%d\n",l,a[l],id,lc0[id],rc0[id]);
}
else{
lazy[id]^=1;
swap(len0[id],len3[id]);
swap(lc0[id],lc3[id]);
swap(rc0[id],rc3[id]);
}
}
void push_down(int id,int l,int r){
// printf("id=%d l=%d r=%d lazy=%d\n",id,l,r,lazy[id]);
if(lazy[id]==0) return ;
int mid=(l+r)>>1;
change(id<<1,l,mid);
change(id<<1|1,mid+1,r);
lazy[id]=0;
}
void update(int id,int l,int r,int x,int y){
// printf("update id=%d l=%d r=%d x=%d y=%d\n",id,l,r,x,y);
if(x>r||y<l) return ;
if(x<=l&&y>=r){
change(id,l,r);
// printf("updatein lc0[%d]=%d rc0[%d]=%d a[%d]=%d\n",id,lc0[id],id,rc0[id],l,a[l]);
return ;
}
push_down(id,l,r);
int mid=(l+r)>>1;
if(x<=mid) update(id<<1,l,mid,x,y);
if(y>mid) update(id<<1|1,mid+1,r,x,y);
push_up(id);
}
int query(int id,int l,int r,int x,int y){
if(x<=l&&y>=r) return len0[id];
// printf("ans1 id=%d l=%d r=%d x=%d y=%d\n",id,l,r,x,y);
int mid=(l+r)>>1,ans=0;
push_down(id,l,r);
//区间合并
if(y<=mid) return query(id<<1,l,mid,x,y);
else if(x>mid) return query(id<<1|1,mid+1,r,x,y);
else{
int ans1 =query(id<<1,l,mid,x,y);
int ans2 =query(id<<1|1,mid+1,r,x,y);
int ans3 =0;
if(lc0[id<<1|1]!=inf&&rc0[id<<1]!=0) ans3 = min(y,lc0[id<<1|1])-max(rc0[id<<1],x);
//在min(y,lc0[id<<1|1]) max(x,rc0[id<<1]) 这个是因为求的范围是 x y,但是这个位置可能大于y或者小于x,所以这里要做一个限制
// printf("id=%d ans1=%d ans2=%d ans3=%d\n",id,ans1,ans2,ans3);
return max(ans1,max(ans2,ans3));
}
}
int querylc(int id,int l,int r,int x,int y){
if(x<=l&&y>=r) return lc0[id];
int mid=(l+r)>>1,ans=inf;
// printf("ans2 id=%d\n",id);
push_down(id,l,r);
if(x<=mid) ans=min(ans,querylc(id<<1,l,mid,x,y));
if(y>mid) ans=min(ans,querylc(id<<1|1,mid+1,r,x,y));
return ans;
}
int queryrc(int id,int l,int r,int x,int y){
if(x<=l&&y>=r) return rc0[id];
int mid=(l+r)>>1,ans=0;
// printf("ans3 id=%d\n",id);
push_down(id,l,r);
if(x<=mid) ans=max(ans,queryrc(id<<1,l,mid,x,y));
if(y>mid) ans=max(ans,queryrc(id<<1|1,mid+1,r,x,y));
return ans;
}
void modify(int id,int l,int r,int pos,int f){
if(l==r){
// printf("before f=%d a[%d]=%d\n",f,l,a[l]);
//无论这个位置是什么都要注意状态的更新,是0,则也要更新3的状态
if(f==1){
if(a[l]==3) a[l]=1,lc3[id]=inf,rc3[id]=0;
else if(a[l]==1) a[l]=3,lc3[id]=l,rc3[id]=l;
else if(a[l]==0) a[l]=2,lc0[id]=inf,rc0[id]=0;
else if(a[l]==2) a[l]=0,lc0[id]=l,rc0[id]=r;
}
else{
if(a[l]==3) a[l]=2,lc3[id]=inf,rc3[id]=0;
else if(a[l]==2) a[l]=3,lc3[id]=l,rc3[id]=l;
else if(a[l]==0) a[l]=1,lc0[id]=inf,rc0[id]=0;
else if(a[l]==1) a[l]=0,lc0[id]=l,rc0[id]=r;
}
// printf("f=%d a[%d]=%d\n",f,l,a[l]);
return ;
}
// printf("ss id=%d l=%d r=%d\n",id,l,r);
int mid=(l+r)>>1;
push_down(id,l,r);
if(pos<=mid) modify(id<<1,l,mid,pos,f);
if(pos>mid) modify(id<<1|1,mid+1,r,pos,f);
push_up(id);
}
void push_alldown(int id,int l,int r){
if(l==r) return ;
// printf("id=%d ddd\n",id);
push_down(id,l,r);
int mid=(l+r)>>1;
push_alldown(id<<1,l,mid);
push_alldown(id<<1|1,mid+1,r);
}
void print(int n){
for(int i=1;i<=n;i++) printf("a[%d]=%d\t",i,a[i]);
printf("\n");
}
int main(){
int n,m;
memset(lc0,inf,sizeof(lc0));
memset(lc3,inf,sizeof(lc3));
scanf("%d%d",&n,&m);
scanf("%s",s+1);
if(n==1){
while(m--) printf("%d\n",1);
return 0;
}
for(int i=1;i<n;i++){
if(s[i]=='>'&&s[i+1]=='<') a[i]=3;
if(s[i]=='<'&&s[i+1]=='>') a[i]=0;
if(s[i]=='>'&&s[i+1]=='>') a[i]=1;
if(s[i]=='<'&&s[i+1]=='<') a[i]=2;
}
build(1,1,n-1);
// print(n-1);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
update(1,1,n-1,x,y-1);
if(x>1) modify(1,1,n-1,x-1,1);
if(y<n) modify(1,1,n-1,y,2);
// printf("xxx\n");
// push_alldown(1,1,n-1);
// print(n-1);
if(x==y){
printf("1\n");
continue;
}
int ans=query(1,1,n-1,x,y-1);
int lc=querylc(1,1,n-1,x,y-1);
int rc=queryrc(1,1,n-1,x,y-1);
// printf("\n\n");
if(rc==0) printf("%d\n",y-x+1);
else{
// printf("lc=%d rc=%d ans=%d\n",lc,rc,ans);
ans=max(ans,max(y-rc,lc-x+1));
printf("%d\n",ans);
}
}
}