码迷,mamicode.com
首页 > 其他好文 > 详细

F. Raging Thunder 线段树 + 区间合并

时间:2020-07-10 20:46:11      阅读:46      评论:0      收藏:0      [点我收藏+]

标签:结束   wap   ns3   结果   oid   set   维护   间隔   包含   

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);
        }
    }
}

F. Raging Thunder 线段树 + 区间合并

标签:结束   wap   ns3   结果   oid   set   维护   间隔   包含   

原文地址:https://www.cnblogs.com/EchoZQN/p/13281087.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!