[CDQ分治][Treap][树状数组]Theresa与数据结构

Description

这是个复杂的世界。人类社会,自然界,还有地球之外的银河……
每一天日出日落,人来人往,步履匆匆。究竟是为什么呢?那支配着一切的至高无上的法则又是否存在呢?Theresa知道,这个问题并不是一朝一夕就可以解答的,只有在仔细、深入的观察和思考以后,才有可能将所有支离破碎的线索联系起来,从而隐约窥见真实的答案。
于是,Theresa经常思考生活中遇到的大大小小的问题。为什么港台出版的书籍里印刷的汉字她一个也不认识呢?为什么隔夜的白开水中富含一氧化二氢呢?为什么每年都有一段时间Gmail邮箱上不去呢?
为了更加系统、科学地分析这些问题,Theresa决定向你求助。
长话短说,Theresa想请你帮助她实现一个数据结构。这个数据结构的功能是在空间直角坐标系中维护一个点的集合,并支持以下三类操作:
1. ADD x y z 加入一个新的点,点的坐标为(x, y, z)。
2. QUERY x y z r 查询在正方体(x, y, z) - (x+r, y+r, z+r)内部的点的数目。
3. CANCEL 撤销最近的一次ADD操作。
其中x, y, z, r均为给出的整数。QUERY操作中,(x, y, z)为正方体的一个顶点的坐标,r为正方体的边长。在正方体边界上的点也算在正方体内部。
这个问题可能过于困难,所以Theresa并不强迫你实现一个高效的数据结构。然而,你必须对每一次QUERY操作给出正确的答案。

Input

第一行包含一个整数N,表示最初的点集有N个点。
接下来N行,每行包含三个整数xi、yi、zi,依次表示每个点的坐标。
第N+2行包含一个整数Q,表示将有Q次操作。
接下来Q行,每行表示一次操作,格式如题目描述。

Output

输出若干行,每行一个整数,依次表示每次查询操作的答案。

Sample Input

21 2 31 1 37ADD 0 4 3QUERY 0 0 0 4ADD 1 1 5QUERY 1 1 2 3QUERY 0 2 2 1CANCELQUERY 1 1 2 3

Sample Output

3312样例解释:第1次查询正方体(0, 0, 0) - (4, 4, 4),内部包含点(1, 2, 3),(1, 1, 3),(0, 4, 3)。第2次查询正方体(1, 1, 2) - (4, 4, 5),内部包含点(1, 2, 3),(1, 1, 3),(1, 1, 5)。第3次查询正方体(0, 2, 2) - (1, 3, 3),内部包含点(1, 2, 3)。第4次查询正方体(1, 1, 2) - (4, 4, 5),内部包含点(1, 2, 3),(1, 1, 3)。 

Data Constraint

对于10%的数据:1 ≤ N, Q ≤ 1 000。
此外10%的数据:0 ≤ x, y, z ≤ 100,1 ≤ r ≤ 100。
此外30%的数据:没有ADD操作和CANCEL操作。
此外20%的数据:没有CANCEL操作。
以上70%的数据:1 ≤ N, Q ≤ 50 000。
对于100%的数据:1 ≤ N + Q ≤ 100 000, 0 ≤ x, y, z, r ≤ 10^7。r 为正整数。   所有的CANCEL操作均为有效操作。不同的点的坐标可能重合。

分析

这题是个四维偏序问题。

时间那维我们用CDQ分治解决

x我们可以排序离散化

y用树状数组,z用Treap(没敢用Splay常数太大233)

那么这题是一个CDQ分治套树套树

(写LowerBound气死了,忘记在上界加个1)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for (i=a;i<=b;i++)
#define lowbit(x) x&-x
const int N=100005;
using namespace std;
struct Node {
    int l,r,key,mak,dat,sum;
}t[50*N];
int tcnt;
struct Point {
    int x,y,z;
}stk[N];
struct Query {
    Point p;
    int r,t,id;
}q[N],a[2*N];
int qcnt;
int rt[N];
int sy,sq,sh,n,m,top;
int ans[N],y[N];
//Treap
void Update(int x) {
    t[x].sum=t[x].dat+t[t[x].l].sum+t[t[x].r].sum;
}

void Left_Rotate(int &x) {
    int s=t[x].r;
    t[x].r=t[s].l;t[s].l=x;
    Update(x);Update(s);
    x=s;
}

void Right_Rotate(int &x) {
    int s=t[x].l;
    t[x].l=t[s].r;t[s].r=x;
    Update(x);Update(s);
    x=s;
}

void Insert(int &x,int mak,int dat) {
    if (!x) {
        x=++tcnt;t[x].l=t[x].r=0;t[x].mak=mak;t[x].sum=t[x].dat=dat;t[x].key=rand();
        return;
    }
    if (mak<t[x].mak) {
        Insert(t[x].l,mak,dat);
        if (t[t[x].l].key>t[x].key) Right_Rotate(x);
    }
    else {
        Insert(t[x].r,mak,dat);
        if (t[t[x].r].key>t[x].key) Left_Rotate(x);
    }
    Update(x);
}

int Get_Suk(int x,int mak) {
    if (!x)    return 0;
    if (mak<t[x].mak) return Get_Suk(t[x].l,mak);
    return Get_Suk(t[x].r,mak)+t[x].dat+t[t[x].l].sum;
}
//Treearray
void Add(int x,int z,int dat) {
    for (int i=x;i<=sy+1;i+=lowbit(i)) Insert(rt[i],z,dat);
}

int Get_Sum(int x,int z,int r) {
    int ans=0;
    for (int i=x;i;i-=lowbit(i)) ans+=Get_Suk(rt[i],z+r)-Get_Suk(rt[i],z-1);
    return ans;
}

void Init() {
    int i;
    char s[20];
    scanf("%d",&n);
    rep(i,1,n) {
        sq++;
        scanf("%d%d%d",&q[sq].p.x,&q[sq].p.y,&q[sq].p.z);
        q[sq].id=sq;q[sq].t=1;
    }
    scanf("%d",&m);
    rep(i,1,m) {
        scanf("%s",&s);
        if (s[0]==‘A‘) {
            sq++;
            scanf("%d%d%d",&q[sq].p.x,&q[sq].p.y,&q[sq].p.z);
            q[sq].id=sq;q[sq].t=1;
            stk[++top]=q[sq].p;
        }
        if (s[0]==‘C‘) {
            sq++;
            q[sq].p=stk[top--];

            q[sq].id=sq;q[sq].t=-1;
        }
        if (s[0]==‘Q‘) {
            sq++;
            scanf("%d%d%d%d",&q[sq].p.x,&q[sq].p.y,&q[sq].p.z,&q[sq].r);
            q[sq].id=sq;q[sq].t=0;
        }
    }
}

bool Cmp(Query a,Query b) {
    return a.p.x<b.p.x||a.p.x==b.p.x&&a.t<b.t;
}

int Lower_Bound(int l,int r,int x) {
    if (x<y[1]) return 1;
    while (l<r) {
        int mid=l+r+1>>1;
        if (y[mid]>x) r=mid-1; else l=mid;
    }
    return l+1;
}

void Fix() {
    int i;
    sort(a+1,a+sh+1,Cmp);
    sy=0;
    rep(i,1,sh)
    if (a[i].t<2) y[++sy]=a[i].p.y;
    sort(y+1,y+sy+1);
    sy=unique(y+1,y+sy+1)-(y+1);
    rep(i,1,sy+1) rt[i]=0;tcnt=0;
    rep(i,1,sh) {
        if (a[i].t<2)
        Add(Lower_Bound(1,sy,a[i].p.y),a[i].p.z,a[i].t);
        else {
            int l=Lower_Bound(1,sy,a[i].p.y-1),r=Lower_Bound(1,sy,a[i].p.y+a[i].r);
            if (l==r) continue;
            ans[a[i].id]+=(Get_Sum(r,a[i].p.z,a[i].r)-Get_Sum(l,a[i].p.z,a[i].r))*(a[i].t-3);
        }
    }
}
bool In(Point a,Point b,int r) {
    return a.x>=b.x&&a.y>=b.y&&a.z>=b.z&&a.x<=b.x+r&&a.y<=b.y+r&&a.z<=b.z+r;
}

void Work(int l,int r) {
    int so=0,sa=0,i,j;
    rep(i,l,r) if (!q[i].t) sa++; else so++;
    if (!sa||!so) return;
    if (r-l+1<=400) {
        rep(i,l,r-1)
        rep(j,i+1,r)
        if (!q[j].t&&In(q[i].p,q[j].p,q[j].r)) ans[q[j].id]+=q[i].t;
        return;
    }
    int mid=l+r>>1;
    if (l==1&&r==sq) mid=n;
    Work(l,mid);
    Work(mid+1,r);
    sh=0;
    rep(i,l,mid) if (q[i].t!=0) a[++sh]=q[i];
    int hs=sh;
    rep(i,mid+1,r)
    if (!q[i].t) {
        a[++sh]=q[i];a[sh].p.x--;a[sh].t=2;
        a[++sh]=q[i];a[sh].p.x+=q[i].r;a[sh].t=4;
    }
    if (hs&&sh>hs) Fix();
}

void Print() {
    int i;
    rep(i,1,sq)
    if (!q[i].t) printf("%d\n",ans[q[i].id]);
}

int main() {
    Init();
    Work(1,sq);
    Print();
}

原文地址:https://www.cnblogs.com/mastervan/p/9404416.html

时间: 2024-10-07 03:05:25

[CDQ分治][Treap][树状数组]Theresa与数据结构的相关文章

UVA 11990 `Dynamic&#39;&#39; Inversion CDQ分治, 归并排序, 树状数组, 尺取法, 三偏序统计 难度: 2

题目 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3141 题意 一个1到n的排列,每次随机删除一个,问删除前的逆序数 思路 综合考虑,对每个数点,令value为值,pos为位置,time为出现时间(总时间-消失时间),明显是统计value1 > value2, pos1 < pos2, time1 < time2的个

CF833D Red-Black Cobweb 点分治、树状数组

传送门 统计所有路径的边权乘积的乘积,不难想到点分治求解. 边权颜色比例在\([\frac{1}{2},2]\)之间,等价于\(2B \geq R , 2R \geq B\)(\(R,B\)表示红色和黑色的边的条数) 所以我们可以在统计的时候,先把所有可能的路径全部乘进答案,然后除掉满足\(2B < R\)或者\(2R < B\)的路径的乘积.显然对于一条路径,这两个条件至多满足一个. 对于两条路径,它们红色.黑色的边数分别为\(B_1,R_1\)和\(B_2,R_2\),那么需要统计的就是\

Poj 2182-Lost Cows(Treap||树状数组+二分答案)

题目链接:点击打开链接 题意:n个牛编号为1-n 现在编号顺序已经打乱,给出a[i] ,a[i] 代表i位置前面有几个小于它的编号,求编号顺序. 倒着推,对于最后一个a[i] , 最后位置编号肯定是 a[i]+1,然后在1-n个编号中删掉当前编号,继续往前推..即求第 a[i]+1小数,初始容器中有n个数(1-n) ,每求出来一个就删掉.先用平衡树水了一发..明天写树状数组解法. #include <algorithm> #include <iostream> #include &

CDOJ 838 母仪天下 树状数组 (2014数据结构专题

母仪天下 Time Limit: 1 Sec  Memory Limit: 162 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/838 Description 富庶的建业城中,有一条格格不入的长街,名曰跳蚤街,被战争所致的孤儿,聚集于此.全国的经济都在为战争服务之时,也无人顾得了这里了. 除了两位夫人. 大乔小乔每天都会带着一些食物来到跳蚤街,分给某一位孩子.为了避免分配不均,她们时常会询问一个区域内食物的总量,然后进行调整以保证每个孩子都有足够

BZOJ 3173 Tjoi2013 最长上升子序列 Treap+树状数组

题目大意:给定一个序列,依次将1~n插入,问每次插入之后序列的LIS长度是多少 由于是从小到大插入,因此插入一个数之后显然是不影响之前的答案的 因此我们不妨先用平衡树搞出插入之后的序列,再求一遍LIS即可 注意最后每个点还要对前面的取一下max 因为插入后LIS可能还是之前的序列 蒟蒻的我到底还是把平衡树写挂了... #include <cstdio> #include <cstring> #include <iostream> #include <algorit

hdu_5324_Boring Class(cdq分治+树状数组)

题目链接:hdu_5324_Boring Class 题意: 给出n个二维点对,求LIS长度和编号字典序最小的LIS(x非增,y非减) 题解: dp[i]=max(dp[j]) (i>j,l[i]>=l[j],r[i]<=r[i]) 一看就是三维偏序问题. 如果树套树写的好,空间开的大的话,一样可以过,不过这里还是用cdq分治套树状数组好写一点. 用lowbit来维护dp[j]的最大值,然后因为要字典序最小,所以从后往前dp. 1 #include<bits/stdc++.h>

【树状数组】【权值分块】bzoj2352 Stars

经典问题:二维偏序.给定平面中的n个点,求每个点左下方的点的个数. 因为 所有点已经以y为第一关键字,x为第二关键字排好序,所以我们按读入顺序处理,仅仅需要计算x坐标小于<=某个点的点有多少个就行. 这就是所说的:n维偏序,一维排序,二维树状数组,三维 分治 Or 树状数组套平衡树…… <法一>树状数组. 1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using name

经典算法题每日演练——第十题 树状数组

原文:经典算法题每日演练--第十题 树状数组 有一种数据结构是神奇的,神秘的,它展现了位运算与数组结合的神奇魅力,太牛逼的,它就是树状数组,这种数据结构不是神人是发现不了的. 一:概序 假如我现在有个需求,就是要频繁的求数组的前n项和,并且存在着数组中某些数字的频繁修改,那么我们该如何实现这样的需求?当然大家可以往 真实项目上靠一靠. ① 传统方法:根据索引修改为O(1),但是求前n项和为O(n). ②空间换时间方法:我开一个数组sum[],sum[i]=a[1]+....+a[i],那么有点意

XJOI NOIP2015模拟赛Day1 T2 ctps bitset优化 或 排序+cdq分治+树状数组+平衡树

题意: 4维空间中有1个点集A,|A|=n,用(a,b,c,d)表示每个点. 共有m个询问,每次询问输入一个点(a,b,c,d),求最大的S,其中S={p|p∈A且ap<=a,bp<=b,cp<=c,dp<=d},输出|S| 输入格式: 第一行n 接下来n行有n个4维点对 第n+2行有一个数m 再接下来m行每行有一个四维点对,表示每个询问 输出格式: 对于每个询问输出一个数 **方法:**bitset优化 或 排序+cdq分治+树状数组+平衡树 解析: 神题,考场不会,暴力骗40,