[BZOJ]4170: 极光 2989: 数列 CDQ分治+树状数组

Description
给定一个长度为n的正整数数列a[i]。
定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]|。
2种操作(k都是正整数):
1.Modify x k:将第x个数的值修改为k。
2.Query x k:询问有几个i满足graze(x,i)<=k。因为可持久化数据结构的流行,询问不仅要考虑当前数列,还要考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的a[x]的graze值<=k的对数。(某位置多次修改为同样的数值,按多次统计)

题解:

一开始看,分四种情况讨论的话是四维偏序,后来灵机一动,把它看做平面上的点的话,求的就是距离不超过k的有多少个点,这个区域恰好是一个菱形,那么把坐标系旋转一下,就可以变成询问一个矩形内数的和了,那么就和简单题的方法一样了。(不知道为什么数组要莫名开大)

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxq=150010;
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<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,Q,a[600100],cnt,idcnt=0;
int X(int x,int y){return x+y-1;}
int Y(int x,int y){return n-x+y;}
LL ans[500100];
struct Opt{int x,y,k,type,id;}q[Maxq],q1[Maxq<<2],temp[Maxq<<2];
int s[4000010];
void add(int x,int y){if(x<=0)return;for(;x<=4000000;x+=(x&-x))s[x]+=y;}
LL getsum(int x){LL re=0;for(;x>0;x-=(x&-x))re+=(LL)(s[x]);return re;}
int List[Maxq<<2];
void solve(int l,int r)
{
    if(l==r)return;
    int mid=l+r>>1;
    solve(l,mid);solve(mid+1,r);
    int i=l,j=mid+1,len=0,o=0;
    while(i<=mid&&j<=r)
    {
        if(q1[i].x<q1[j].x||(q1[i].x==q1[j].x&&q1[i].type<q1[j].type))
        {
            if(q1[i].type==1)add(q1[i].y,1),List[++o]=q1[i].y;
            temp[++len]=q1[i++];
        }
        else
        {
            if(q1[j].type==2)ans[q1[j].id]+=getsum(q1[j].y);
            else if(q1[j].type==3)ans[q1[j].id]-=getsum(q1[j].y);
            temp[++len]=q1[j++];
        }
    }
    int t=i;
    while(i<=mid)temp[++len]=q1[i++];
    while(j<=r)
    {
        if(q1[j].type==2)ans[q1[j].id]+=getsum(q1[j].y);
        else if(q1[j].type==3)ans[q1[j].id]-=getsum(q1[j].y);
        temp[++len]=q1[j++];
    }
    for(int p=1;p<=o;p++)add(List[p],-1);
    for(int p=1;p<=len;p++)q1[l+p-1]=temp[p];
}
int main()
{
    n=read(),Q=read();
    for(int i=1;i<=n;i++)q[i].type=1,q[i].x=i,q[i].y=a[i]=read();
    for(int i=1;i<=Q;i++)
    {
        char op[8];
        scanf("%s",op);
        int u1=read(),u2=read(),t=i+n;
        if(op[0]=='M')q[t].type=1,q[t].x=u1,q[t].y=u2,a[u1]=u2;
        else q[t].type=2,q[t].x=u1,q[t].y=a[u1],q[t].k=u2,q[t].id=++idcnt;
    }
    for(int i=1;i<=n;i++)q1[i]=q[i],q1[i].x=X(q[i].x,q[i].y),q1[i].y=Y(q[i].x,q[i].y);cnt=n;
    for(int i=1+n;i<=Q+n;i++)
    {
        if(q[i].type==1)q1[++cnt]=q[i],q1[cnt].x=X(q[i].x,q[i].y),q1[cnt].y=Y(q[i].x,q[i].y);
        else
        {
            int X1=q[i].x,Y1=q[i].y-q[i].k,X2=q[i].x,Y2=q[i].y+q[i].k;
            int x1=X(X1,Y1),y1=Y(X1,Y1),x2=X(X2,Y2),y2=Y(X2,Y2);
            q1[++cnt].type=2;q1[cnt].x=x2;q1[cnt].y=y2;
            q1[++cnt].type=2;q1[cnt].x=x1-1;q1[cnt].y=y1-1;
            q1[++cnt].type=3;q1[cnt].x=x1-1;q1[cnt].y=y2;
            q1[++cnt].type=3;q1[cnt].x=x2;q1[cnt].y=y1-1;
            q1[cnt].id=q1[cnt-1].id=q1[cnt-2].id=q1[cnt-3].id=q[i].id;
        }
    }
    solve(1,cnt);
    for(int i=1;i<=idcnt;i++)printf("%lld\n",ans[i]);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值