BZOJ2989: 数列(二进制分组/KDtree+替罪羊)

15 篇文章 0 订阅
10 篇文章 0 订阅

传送门

题意:
(稍微转化一下)每次加一些点,在一个正方形内查询点的个数。

题解:
先坐标旋转做成矩形查询,然后比较直观的是 KDtree+
Code:https://paste.ubuntu.com/26306229/

还有一种厉害的方法:分治。

比较明显的是可以CDQ分治+ 主席树,另外还可以对这些点对做二进制分组。每次暴力重建的复杂度为 nlogn ,总的复杂度为 O(nlog2n)
后者不仅可以强制在线,而且比较好写,缺点是所占空间很大,如果不加以回收会达到 O(nlog2n)

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
    char ch=getchar();int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
    return i*f;
}
inline void W(int x){
    static int buf[50];
    if(!x){putchar('0');return;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10;x/=10;}
    while(buf[0])putchar(buf[buf[0]--]+'0');
}
const int N=1e5+50,base=1e5+1,R=4e5+4,LIM=20,INF=0x3f3f3f3f,_T=N-2;
struct P{
    int x,y;
    P(int x=0,int y=0):x(x),y(y){}
}p[N];
struct node{
    node *lc,*rc;
    int sze;
}Pool[N*120],*pool=Pool,*rt[N*120],*rt_null;
int n,q,a[N],sze[LIM],bnum;
inline node* newnode(){
    node *t=++pool;
    t->lc=(t->rc=NULL);
    t->sze=0;
    return t;
}
vector<int>con[LIM];
inline bool cmp_p(const int &a,const int &b){
    return p[a].x<p[b].x||(p[a].x==p[b].x&&p[a].y<p[b].y);
}
inline void build(node *&x,int l,int r){
    x=newnode();
    if(l==r)return;
    int mid=(l+r)>>1;
    build(x->lc,l,mid);
    build(x->rc,mid+1,r);
}
inline void insert(node *x,node *&y,int l,int r,int pos){
    y=newnode();y->sze=x->sze+1;
    y->lc=x->lc;y->rc=x->rc;
    if(l==r)return;
    int mid=(l+r)>>1;
    (pos<=mid)?
    (insert(x->lc,y->lc,l,mid,pos)):
    (insert(x->rc,y->rc,mid+1,r,pos));
}
inline void rebuild(int c){
    con[c].clear();
    int bg=sze[c]-(sze[c]&(-sze[c]))+1;
    for(int i=bg;i<=sze[c];++i)con[c].push_back(i);
    sort(con[c].begin(),con[c].end(),cmp_p);
    insert(rt_null,rt[*con[c].begin()],1,R,p[*con[c].begin()].y);
    for(int i=1;i<con[c].size();++i)
        insert(rt[con[c][i-1]],rt[con[c][i]],1,R,p[con[c][i]].y);
}
int xc1,yc1,xc2,yc2;
inline int query(node *x,node *y,int l,int r,int L,int R){
    if(L<=l&&r<=R)return y->sze-x->sze;
    int mid=(l+r)>>1;
    if(R<=mid)return query(x->lc,y->lc,l,mid,L,R);
    else if(L>mid)return query(x->rc,y->rc,mid+1,r,L,R);
    else return query(x->lc,y->lc,l,mid,L,R)+query(x->rc,y->rc,mid+1,r,L,R);
}
inline int query(int c){
    p[_T]=P(xc2+1,-INF);
    int p1=lower_bound(con[c].begin(),con[c].end(),_T,cmp_p)-con[c].begin();
    if(!p1)return 0;
    p[_T]=P(xc1,-INF);
    int p2=lower_bound(con[c].begin(),con[c].end(),_T,cmp_p)-con[c].begin();
    return query(p2?(rt[con[c][p2-1]]):rt_null,rt[con[c][p1-1]],1,R,yc1,yc2);
}
int main(){
    n=rd(),q=rd();
    build(rt_null,1,R);
    for(int i=1;i<=n;i++){
        a[i]=rd();
        p[i]=P(i-a[i]+base,i+a[i]+base);
    }
    for(int i=LIM;i>=0;i--)
        if(n&(1<<i))
            sze[++bnum]=(1<<i);
    for(int i=1;i<=bnum;i++)sze[i]+=sze[i-1];
    for(int i=1;i<=bnum;i++)rebuild(i);
    for(int i=1;i<=q;i++){
        static char op[10];
        scanf("%s",op+1);
        if(op[1]=='Q'){
            int x=rd(),k=rd();
            xc1=x-a[x]+k+base;xc2=x-a[x]-k+base;
            yc1=x+a[x]+k+base;yc2=x+a[x]-k+base;
            (xc1>xc2)&&(swap(xc1,xc2),0);
            (yc1>yc2)&&(swap(yc1,yc2),0);
            int ans=0;
            for(int c=1;c<=bnum;++c)
                ans+=query(c);
            W(ans);putchar('\n');
        }else{
            int x=rd(),y=rd();a[x]=y;
            p[++n]=P(x-y+base,x+y+base);
            int lst=1;
            while(bnum&&(sze[bnum]&(-sze[bnum]))==lst){
                --bnum;lst<<=1;
            }
            sze[++bnum]=lst;
            sze[bnum]+=sze[bnum-1];
            rebuild(bnum);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值