jzoj3304. Theresa与数据结构(cdq分治+扫描线+带修主席树)

题目

  • 3个操作
  • 1:在当前空间加入一个权值为1,坐标为 (x,y,z) ( x , y , z ) 的点
  • 2:同1,权值为-1
  • 3:查询当前空间中, (x,y,z) ( x , y , z ) ~ (x+r,y+r,z+r) ( x + r , y + r , z + r ) 正方体中的点

想法:

  • 猥琐的数据结构~~
  • 先讲讲扫描线,即在二维平面中,已知某些点,查询某个矩形的权值和
  • 先将 x x 坐标从小到大排序,然后遇到一个点,我们就给线段树or树状数组对应的位置加上相应的权值,对于查询矩阵( x1 x 1 ~ x2 x 2 )( y1 y 1 ~ y2 y 2 ),我们就可以用 ans a n s 1 1 ~x2)( y1 y 1 ~ y2 y 2 )- ans a n s 1 1 ~x11)( y1 y 1 ~ y2 y 2 ),即在扫描到 x11 x 1 − 1 时求出此时( y1 y 1 ~ y2 y 2 )的答案,再扫描到 x2 x 2 时求出此时( y1 y 1 ~ y2 y 2 )的答案,相减即可
  • 万一边加点边查询呢,如何解决
  • 考虑cdq分治
  • 我们可以先以时间为第一关键字排序,然后对于区间 (l ( l ~ r) r ) ,当前仅仅考虑 (l ( l ~ mid) m i d ) 中加点操作
  • (mid+1 ( m i d + 1 ~ r) r ) 中询问的影响,然后用扫描线解决,在递归进入 (l ( l ~ mid) m i d ) (mid+1 ( m i d + 1 ~ r) r ) 分别解决子问题
  • 其实可以在cdq分治时用归并排序对x排序,到时加点判断时间 <=mid <= m i d <script type="math/tex" id="MathJax-Element-95"><=mid</script>才做,查询 >mid > m i d 才做,优化时间
  • 然后在考虑这题
  • 一种是cdq套cdq,但我不会~
  • 还有一种是用cdq对时间分治,
  • 用归并排序对x排序,进行扫描线
  • 然后遇到点 (x,y,z) ( x , y , z ) ,就在平面 (y,z) ( y , z ) 加上相应的权值,对于询问 (x1 ( x 1 ~ x2,y1 x 2 , y 1 ~ y2,z1 y 2 , z 1 ~ z2) z 2 ) ,就相当于求 ans(1 a n s ( 1 ~ x2,y1 x 2 , y 1 ~ y2,z1 y 2 , z 1 ~ z2)ans(1 z 2 ) − a n s ( 1 ~ x11,y1 x 1 − 1 , y 1 ~ y2,z1 y 2 , z 1 ~ z2) z 2 ) 然后我们需要一个二维数据结构支持插入和询问
  • 带修主席树是一个好东东
  • 如果单纯用主席树,那么我们要给 (y ( y ~ n) n ) 的线段树的第 z z 位加上相应的权值,时间复杂度巨大~~
  • 主席树的第i棵存储的是1~ i i 的信息,那么我们可以用树状数组套线段树
  • 对于第i棵线段树,我们存储的是ilowbit(i)+1~ i i 的信息(树状数组),这样就不断修改第i+lowbit(i)棵线段树,最后查询同理
  • 记得离散化
    时间复杂度( nlog3n n l o g 3 n )
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxN=2e5+10,maxx=31250000,maxn=1e7;
struct zhj{
    int x,y,z,zz,yy,zl,wz;
}a[maxN],b[maxN],c[maxN*2];
struct xkq{
    int x,l,r;
}tr[maxx];
char ch;
int n,m,i,j,pre[maxN],answer[maxN],fro[maxN],q,tot,cnt,zl,ans,
xxl,xxr,yyl,yyr,sum,rr,mz,my,tail,tail1,root[maxN];
bool cmp(zhj x,zhj y){
    return ((x.x<y.x) ||(x.x==y.x && x.zl<y.zl));
} 
void scan(){
    scanf("%d",&n);
    fo(i,1,n) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].zl=1,a[i].wz=i,pre[++pre[0]]=i,my=max(a[i].y,my),mz=max(a[i].z,mz);
    scanf("%d\n",&q),cnt=n;
    fo(i,1,q){
        scanf("%c",&ch);
        if (ch=='A'){
            cnt++;
            scanf("DD %d%d%d\n",&a[cnt].x,&a[cnt].y,&a[cnt].z);
            a[cnt].wz=cnt,a[cnt].zl=1,pre[++pre[0]]=cnt;
            my=max(a[cnt].y,my),mz=max(a[cnt].z,mz);
        }
        if (ch=='C'){
            cnt++;
            scanf("ANCEL\n");
            a[cnt]=a[pre[pre[0]]];
            a[cnt].wz=cnt,a[cnt].zl=-1;
            pre[0]--;
        }
        if (ch=='Q') {
            cnt++;
            scanf("UERY %d%d%d%d\n",&a[cnt].x,&a[cnt].y,&a[cnt].z,&rr);
            a[cnt].wz=cnt,a[cnt].zl=2,fro[cnt]=++answer[0],a[cnt].yy=a[cnt].y+rr,a[cnt].zz=a[cnt].z+rr;
            a[cnt].x--;
            a[cnt+1]=a[cnt];
            a[++cnt].x+=rr+1,a[cnt].wz=cnt,a[cnt].zl=3,fro[cnt]=answer[0];
        }
    } 
    fo(i,1,cnt)
        a[i].yy=min(a[i].yy,my),a[i].zz=min(a[i].zz,mz);
    tot=0;
    fo(i,1,cnt){
        c[++tot].x=a[i].y,c[tot].wz=i,c[tot].zl=0;
        if (a[i].zl>1) 
            c[++tot].x=a[i].yy,c[tot].wz=i,c[tot].zl=1;
    }
    sort(c+1,c+tot+1,cmp);
    if (c[1].zl==0) a[c[1].wz].y=sum=1;
    else a[c[1].wz].yy=sum=1;
    fo(i,2,tot){
        if (c[i].x>c[i-1].x) sum++;
        if (c[i].zl==0) a[c[i].wz].y=sum;
        else a[c[i].wz].yy=sum;
    }
    my=sum,tot=0;
    fo(i,1,cnt){
        c[++tot].x=a[i].z,c[tot].wz=i,c[tot].zl=0;
        if (a[i].zl>1) 
            c[++tot].x=a[i].zz,c[tot].wz=i,c[tot].zl=1;
    }
    sort(c+1,c+tot+1,cmp);
    if (c[1].zl==0) a[c[1].wz].z=sum=1;
    else a[c[1].wz].zz=sum=1;
    fo(i,2,tot){
        if (c[i].x>c[i-1].x) sum++;
        if (c[i].zl==0) a[c[i].wz].z=sum;
        else a[c[i].wz].zz=sum;
    }
    mz=sum;
}
int lowbit(int x){
    return x&(-x);
}
void insert(int x,int head,int tail){
    tr[x].x+=zl;
    if (head==tail) return;
    int mid=(head+tail)/2;
    if (yyl<=mid){
        if (!tr[x].l) tr[x].l=++cnt;
        insert(tr[x].l,head,mid);
    }else{
        if (!tr[x].r) tr[x].r=++cnt;
        insert(tr[x].r,mid+1,tail);
    }
}
void query(int x,int head,int tail){
    if (yyl<=head && tail<=yyr){sum+=tr[x].x;return;}
    int mid=(head+tail)/2;
    if (yyl<=mid && tr[x].l) query(tr[x].l,head,mid);
    if (mid<yyr && tr[x].r) query(tr[x].r,mid+1,tail);
}
void ins(int x){
    while (x<=my){
        if(!root[x]) root[x]=++cnt,pre[++pre[0]]=x;
        insert(root[x],1,mz);
        x+=lowbit(x);
    }
}
int find(int x){
    sum=0;
    while (x){
        if (root[x])
            query(root[x],1,mz);
        x-=lowbit(x);
    }
    return sum;
}
void work(int x){
    cnt=0;
    fo(i,1,tot){
        if (b[i].zl==1 || b[i].zl==-1){
            if (b[i].wz>x) continue;
            xxl=xxr=b[i].y,yyl=yyr=b[i].z,zl=b[i].zl;
            ins(xxl); 
        }else{
            if (b[i].wz<=x) continue;
            xxl=b[i].y,xxr=b[i].yy,yyl=b[i].z,yyr=b[i].zz,zl=0,ans=0;
            ans=find(xxr)-find(xxl-1);
            if (b[i].zl==2)
                answer[fro[b[i].wz]]-=ans;
            else answer[fro[b[i].wz]]+=ans;
        }
    }
    fo (i,1,cnt) tr[i].x=tr[i].l=tr[i].r=0;
    fo(i,1,pre[0]) root[pre[i]]=0;
    pre[0]=0;
}
void cdq(int l,int r){
    if (l==r)return;
    int mid=(l+r)/2,i,cnt1=0,cnt2=0,cnt3=0;
    fo(i,l,mid)
        if (a[i].zl==1 || a[i].zl==-1) cnt3++;
    fo(i,mid+1,r)
        if (a[i].zl>1) cnt2++;
    cdq(l,mid);
    cdq(mid+1,r);
    tail=l,tail1=mid+1,cnt1=0;
    while (tail<=mid || tail1<=r){
        if (tail>mid) b[++cnt1]=a[tail1],tail1++;
        else if (tail1>r) b[++cnt1]=a[tail],tail++;
        else{
            if (a[tail].x<=a[tail1].x) b[++cnt1]=a[tail],tail++;
            else b[++cnt1]=a[tail1],tail1++;
        }
    }
    fo(i,l,r) a[i]=b[i-l+1];
    tot=cnt1;
    if (!(cnt3 && cnt2)) return;
    work(mid);
}
void prin(){
//  printf("%d\n",sum);
    fo(i,1,answer[0])
        printf("%d\n",answer[i]);
}
int main(){
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scan(); 
    cdq(1,cnt);
    prin();
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值