Tunnel Warfare (鬼子进村)(ZKW线段树)

题目链接(洛谷)

题目链接(HDU)

题目链接(vjudge)

由于题意简单,所以不解释为何用线段树,我用的是重口味线段树。

三方探查过后,大概可以把题意归纳为:

最开始所有点为1。

然后有一种操作,将此点置0(单点修改),这个应该都会。

第二种操作,将上一个被修改的点修复(单点修改),有时候会连续修好几个,所以你需要一个栈。

第三种操作,求与一个点相连的最长(区间查询),我求的是这个点左边最长1和右边最长1,然后相加。

二话不说上代码:

#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define M (50005)
void Read(LL &x) {
    x=0;
    LL f=1;
    char c=getchar();
    while(c<'0'||c>'9') {
        if(c=='-')
            f=-f;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    x*=f;
    return ;
}
LL le[M<<2],ri[M<<2],n,m=1,t,q,len[M<<2];
stack<LL>s;
void Build() {
    for(int i=m - 1; i>=1; i--)
        le[i]=le[i*2]+le[i*2+1],ri[i]=ri[i*2]+ri[i*2+1],len[i]=(len[i*2]+len[i*2+1]);
    return ;
}
void Add(LL k,bool op) {
    k+=m;
    le[k]=ri[k]=op;
    while(k) {
        if(k%2) {
        	k--;
        }     
        	if(ri[k+1]==len[k+1])
        		ri[k/2]=ri[k+1]+ri[k];
        	else
        		ri[k/2]=ri[k+1];
        	if(le[k]==len[k])
        		le[k/2]=le[k+1]+le[k];
        	else
        		le[k/2]=le[k];
        k>>=1;
    }
    return ;
}
void Query(LL L,LL R,LL &x,LL &y) {
    LL s=m+L-1,t=m+R+1,ln=0,rn=0;
    LL l1=0,l2=0,r1=0,r2=0;//不多解释,也就是左边的左,右边的左,右边的左,右边的右。因为左右不一定相连。
    while(s||t) {
        if(s>>1!=t>>1) {
            if(!(s%2)) {
                if(l1==ln)
                    l1=ln+le[s+1];
                if(ri[s+1]==len[s+1])
                	r1+=len[s+1];
                else
                	r1=ri[s+1];
                ln+=len[s+1];
            }
            if(t%2) {
                if(r2==rn)
                    r2=rn+ri[t-1];
                if(le[t-1]==len[t-1])
                	l2+=le[t-1];
                else
                	l2=le[t-1];
                rn+=len[t-1];
            }
        }
        s>>=1;
        t>>=1;
    }
    if(l1==ln)
    	x=l1+l2;
    else
    	x=l1;
    if(r2==rn)
    	y=r2+r1;
    else
    	y=r2;
    return ;
}
int main() {
    while(scanf("%lld %lld",&n,&q)==2) {
        while(!s.empty())
            s.pop();
        memset(le,0,sizeof(le));
        memset(ri,0,sizeof(ri));
        memset(len,0,sizeof(len));
        m=1;
        while(m < n + 2)
            m <<= 1;
        for(int i = 1; i <= n; i ++)
            le[m+i]=1,ri[m+i]=1,len[m+i]=1;
        Build();
        char ck[10];
        while(q--) {
            scanf("%s",ck);
            if(ck[0]=='R') {
                if(s.empty())
                    continue;
                Add(s.top(),1);
                s.pop();
                continue;
            }
            LL a;
            Read(a);
            if(ck[0]=='Q'){
                if(!ri[a+m]){//判断这个点为不为0,
                    puts("0");
                    continue;
                }
                LL l1=0,l2=0,r1=0,r2=0;
                Query(1,a-1,l1,r1);
                Query(a+1,n,l2,r2);
                printf("%lld\n",r1+l2+1);
            }
            if(ck[0]=='D') {
                Add(a,0);
                s.push(a);
            }
        }
    }
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值