【bzoj 2989】数列

传送门~

解题思路

可将 A[i] A [ i ] 抽象为二维平面上的点 (i,A[i]) ( i , A [ i ] ) ,询问就是求与给定点曼哈顿距离小于等于 k k 的点数,修改则是加点操作。
把曼哈顿距离转化为切比雪夫距离,询问就变成了给定(x,y),转为切比雪夫距离后为 (x+y,xy) ( x + y , x − y ) ,求矩形 (x+yk,x+y+k,xyk,xy+k) ( x + y − k , x + y + k , x − y − k , x − y + k ) 内部有多少点。
于是可以使用 CDQ C D Q 分治,也可以大力 KDtree K D − t r e e
KDtree K D − t r e e 代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<set>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstdlib>
#define ll long long
using namespace std;
const int inf=100005;
const double alpha=0.75;
int tmp[100005],a[100005];
int n,m,cmpd,rot,tot;
struct tree{
    int d[2],mx[2],mn[2];
    int siz,ls,rs;
    void clr(){
        siz=1;ls=rs=0;
        mx[0]=mn[0]=d[0];
        mx[1]=mn[1]=d[1];
    }
}s[100005];
bool operator < (const tree r1,const tree r2){
    return r1.d[cmpd]==r2.d[cmpd] ? r1.d[cmpd^1]<r2.d[cmpd^1] : r1.d[cmpd]<r2.d[cmpd];
}
bool cmp(int r1,int r2){return s[r1]<s[r2];}
void pushup(int x,int k){
    s[x].siz+=s[k].siz;
    s[x].mx[0]=max(s[x].mx[0],s[k].mx[0]);
    s[x].mx[1]=max(s[x].mx[1],s[k].mx[1]);
    s[x].mn[0]=min(s[x].mn[0],s[k].mn[0]);
    s[x].mn[1]=min(s[x].mn[1],s[k].mn[1]);
}
int build(int l,int r,int D){
    int mid=l+r>>1;cmpd=D;
    nth_element(tmp+l,tmp+mid,tmp+r+1,cmp);
    int re=tmp[mid];
    if(l<mid) pushup(re,s[re].ls=build(l,mid-1,D^1));
    if(mid<r) pushup(re,s[re].rs=build(mid+1,r,D^1));
    return re;
}
void dfs(int x){
    if(!x) return ;
    dfs(s[x].ls);dfs(s[x].rs);
    tmp[++tot]=x;s[x].clr();
}
void rebuild(int &p,int D){
    tot=0;dfs(p);
    p=build(1,tot,D);
}
void Insert(int &p,int k,int D,bool flag){
    if(!p) {p=k;return ;}
    pushup(p,k);cmpd=D;
    if(s[k]<s[p]){
        if(!flag && s[s[p].ls].siz+1>alpha*s[p].siz) {Insert(s[p].ls,k,D^1,1);rebuild(p,D);}
        else Insert(s[p].ls,k,D^1,flag);
    }
    else {
        if(!flag && s[s[p].rs].siz+1>alpha*s[p].siz) {Insert(s[p].rs,k,D^1,1);rebuild(p,D);}
        else Insert(s[p].rs,k,D^1,flag);
    }
}
int query(int p,int x,int dx,int y,int dy){
    if(!p || x>s[p].mx[0] || dx<s[p].mn[0] || y>s[p].mx[1] || dy<s[p].mn[1]) return 0;
    if(x<=s[p].mn[0]&&dx>=s[p].mx[0]&&y<=s[p].mn[1]&&dy>=s[p].mx[1]) return s[p].siz;
    int re=0;
    if(s[p].d[0]>=x&&s[p].d[0]<=dx&&s[p].d[1]>=y&&s[p].d[1]<=dy) re++;
    re+=query(s[p].ls,x,dx,y,dy)+query(s[p].rs,x,dx,y,dy);
    return re;
}
int main(){
    int x,k;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        s[i].d[0]=i+a[i];
        s[i].d[1]=i-a[i];
        s[i].clr();
        tmp[i]=i;
    }
    rot=build(1,n,0);char opt[10];
    while(m--){
        scanf("%s%d%d",opt,&x,&k);
        if(opt[0]=='M'){
            a[x]=k;n++;s[n].d[0]=x+k;s[n].d[1]=x-k;s[n].clr();
            Insert(rot,n,0,0);
        }
        else printf("%d\n",query(rot,x+a[x]-k,x+a[x]+k,x-a[x]-k,x-a[x]+k));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值