[JZOJ5543] 超越潜能

Description

这里写图片描述
这里写图片描述

Solution

转化题意
坐标为偶数保证了不会停留在坐标轴上

首先可以发现两维坐标是独立的

那么分开考虑,令S为坐标前缀和

那么第i操作后能造成伤害的条件是
S[i]S[i1]<0 ,也可以写成 min(S[i],S[i1])<0<max(S[i],S[i1])

既然题目是指针左移右移,那么这肯定是有用的

设一个变量Tag表示当前指针到右端点的数都加上了tag

考虑在修改的时候维护答案。
设w为当前指针位置
不妨计算答案的增量,很明显就是w与w-1造成的贡献与[w,n]这段的贡献

我们要统计有多少 i>w,min(S[i],S[i1])<0<max(S[i],S[i1])
可以简单的在线段树上区间加,查询0点实现
S都加上了tag,相当于0点左移tag
答案就变成 min(S[i],S[i1])<tag<max(S[i],S[i1])
这次修改又加上了delta
那么就是 min(S[i],S[i1])<tagdelta<max(S[i],S[i1])
线段树单点查询,加上delta的减去不加的就是增量

对于一次右移,我们需要满足指针左边的都是实际值,那么S[w]+=tag,w++,然后线段树中删去这个w和w-1的贡献
对于一次左移,我们需要满足指针右边的都是加上tag以后的值,那么w–,S[w]-=tag,然后线段树中加上w和w+1的贡献

注意线段树的时候c++负数整除实际上是绝对值下取整,要特判

Code

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 100005
#define M 3000005
#define R 50000005
using namespace std;
int a[N][2],n,m,w,t[M][2],s1[M],lz[M],ans,tag1,tag2,sx[N],sy[N],n1;
void down(int k)
{
    s1[t[k][0]]+=lz[k],s1[t[k][1]]+=lz[k];
    lz[t[k][0]]+=lz[k],lz[t[k][1]]+=lz[k];
    lz[k]=0;
}
void ins(int k,int l,int r,int x,int y,int v)
{
    x=max(x,l),y=min(y,r);
    if(x>y||!k) return;
    if(l==x&&r==y) s1[k]+=v,lz[k]+=v;
    else
    {
        int mid=(l+r)/2;
        if(l+r<0) mid+=(l+r)%2;
        if(!t[k][0]) t[k][0]=++n1,t[k][1]=++n1;
        down(k);
        ins(t[k][0],l,mid,x,y,v),ins(t[k][1],mid+1,r,x,y,v);
    }
}
int get(int k,int l,int r,int x)
{
    if(l==r&&x==l) return s1[k];
    int mid=(l+r)/2;
    if(l+r<0) mid+=(l+r)%2;
    if(!t[k][0]) t[k][0]=++n1,t[k][1]=++n1;
    down(k);
    if(x<=mid) return get(t[k][0],l,mid,x);
    else return get(t[k][1],mid+1,r,x);
}
int main()
{
    cin>>n;
    fo(i,1,n) scanf("%d%d",&a[i][0],&a[i][1]);
    cin>>m;
    w=1;
    sx[0]=sy[0]=1;
    n1=2;
    fo(i,1,n) 
    {
        sx[i]=sx[i-1]+a[i][0],sy[i]=sy[i-1]+a[i][1];
        if(i>1) ins(1,-R,R,min(sx[i],sx[i-1]),max(sx[i],sx[i-1]),1);
        if(i>1) ins(2,-R,R,min(sy[i],sy[i-1]),max(sy[i],sy[i-1]),1);
    }
    ans=get(1,-R,R,0)+get(2,-R,R,0)+(sx[1]<0)+(sy[1]<0);
    tag1=tag2=0;
    fo(i,1,m)
    {
        char ch;
        scanf("\n%c",&ch);
        if(ch=='B') 
        {
            if(w!=1)
            {
                sx[w-1]-=tag1,sy[w-1]-=tag2;
                ins(1,-R,R,min(sx[w],sx[w-1]),max(sx[w],sx[w-1]),1);
                ins(2,-R,R,min(sy[w],sy[w-1]),max(sy[w],sy[w-1]),1);
                w--;
            }
        }
        if(ch=='F') 
        {
            if(w!=n)
            {
                ins(1,-R,R,min(sx[w],sx[w+1]),max(sx[w],sx[w+1]),-1);
                ins(2,-R,R,min(sy[w],sy[w+1]),max(sy[w],sy[w+1]),-1);
                sx[w]+=tag1,sy[w]+=tag2;
                w++;
            }
        }
        if(ch=='C') 
        {
            int x,y,vl1,vl2;
            scanf("%d%d",&x,&y);
            vl1=x-a[w][0],vl2=y-a[w][1];
            a[w][0]=x,a[w][1]=y;
            ans+=(get(1,-R,R,-tag1-vl1)-get(1,-R,R,-tag1)+get(2,-R,R,-tag2-vl2)-get(2,-R,R,-tag2));
            ans+=((sx[w]+vl1+tag1)*sx[w-1]<0)-((sx[w]+tag1)*sx[w-1]<0)+((sy[w]+vl2+tag2)*sy[w-1]<0)-((sy[w]+tag2)*sy[w-1]<0);
            tag1+=vl1,tag2+=vl2;
        }
        if(ch=='Q')
        {
            printf("%d\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值