原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2989
数列
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的对数。(某位置多次修改为同样的数值,按多次统计)
Input
第1行两个整数n,q。分别表示数列长度和操作数。
第2行n个正整数,代表初始数列。
第3–q+2行每行一个操作。
Output
对于每次询问操作,输出一个非负整数表示答案
Sample Input
3 5
2 4 3
Query 2 2
Modify 1 3
Query 2 2
Modify 1 2
Query 1 1
Sample Output
2
3
3
HINT
N<=60000 修改操作数<=40000 询问<=50000 Max{a[i]}含修改<=100000
题解
首先,因为我们要考虑所有的历史版本,所以修改的实质其实是添加一个数到数列里。
其次,我们可以将数列表示为平面上的一堆点:将第 i i 个数表示为 (i,ai) ( i , a i ) 。
那么,考虑题目的要求:找有几个点满足 |x1−x2|+|y1−y2|≤k | x 1 − x 2 | + | y 1 − y 2 | ≤ k ,放到平面上,我们可以发现,对于一个点 (x1,y1) ( x 1 , y 1 ) 满足上述条件的点的集合,是一个正方形,以 (6,5) ( 6 , 5 ) , k=3 k = 3 为例:
那么我们要询问的实际上就是,正方形中的点数。
为了便于处理,我们显然要把正方形旋转 45∘ 45 ∘ ,摆成正的,这样每个询问就可以拆成 4 4 个,然后就只需要乖乖即可 AC A C 。
代码
注意判断不在第一象限的情况。
#include<bits/stdc++.h>
#define up(x) MX=max(MX,x)
using namespace std;
const int M=1e6;
struct sd{int op,x,y,val,id,t;};
bool operator <(sd a,sd b)
{
if(a.x!=b.x)return a.x<b.x;
if(a.y!=b.y)return a.y<b.y;
return a.op<b.op;
}
sd ope[M],tmp[M];
int n,q,que[M],ans[M],sum[M],MX,tot,base=1;
bool vis[M];
void off(int op,int id,int x,int y,int k)
{
int xx=x-y,yy=x+y,x1,x2,y1,y2;
if(!op){up(xx),up(yy),ope[++tot]=(sd){op,xx,yy,0,id,tot};}
else
{
x1=xx-k,y1=yy-k,x2=xx+k,y2=yy+k;
if(y1>0)ope[++tot]=(sd){op,x1-1,y1-1,1,id,tot};
ope[++tot]=(sd){op,x1-1,y2,-1,id,tot};
if(y1>0)ope[++tot]=(sd){op,x2,y1-1,-1,id,tot};
ope[++tot]=(sd){op,x2,y2,1,id,tot};
up(x2),up(y2);
}
}
void in()
{
char ch[10];
int a,b;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;++i)scanf("%d",&que[i]),off(0,i,i,que[i],0);
for(int i=n+1;i<=n+q;++i)
{
scanf("%s%d%d",ch,&a,&b);
if(ch[0]=='M'){que[a]=b;off(0,i,a,b,0);}
else off(1,i,a,que[a],b),vis[i]=1;
}
}
void add(int v,int s){v+=base;for(;v;v>>=1)sum[v]+=s;}
int query(int ri)
{
int ans=0,le=base;ri+=base+1;
for(;le^ri^1;le>>=1,ri>>=1)
{
if(le&1^1)ans+=sum[le+1];
if(ri&1)ans+=sum[ri-1];
}
return ans;
}
void cdq(int le,int ri)
{
if(le==ri)return;
int mid=le+ri>>1,p1=le,p2=mid+1;
for(int i=le;i<=ri;++i)
{
if(!ope[i].op&&ope[i].t<=mid)add(ope[i].y,1);
if(ope[i].op&&ope[i].t>mid)ans[ope[i].id]+=ope[i].val*query(ope[i].y);
}
for(int i=le;i<=ri;++i)if(!ope[i].op&&ope[i].t<=mid)add(ope[i].y,-1);
for(int i=le;i<=ri;++i)
if(ope[i].t<=mid)tmp[p1++]=ope[i];
else tmp[p2++]=ope[i];
for(int i=le;i<=ri;++i)ope[i]=tmp[i];
cdq(le,mid);cdq(mid+1,ri);
}
void ac()
{
MX++;while(base<MX)base<<=1;
sort(ope+1,ope+1+tot);cdq(1,tot);
for(int i=1;i<=q+n;++i)if(vis[i])printf("%d\n",ans[i]);
}
int main()
{
in();ac();
return 0;
}