题目描述
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
2
1 2 3
1 1 3
7
ADD 0 4 3
QUERY 0 0 0 4
ADD 1 1 5
QUERY 1 1 2 3
QUERY 0 2 2 1
CANCEL
QUERY 1 1 2 3
Sample Output
3
3
1
2
样例解释:
第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操作均为有效操作。不同的点的坐标可能重合。
10~70%
瞎写
反正这题不需要
100%
显然是四维偏序。。。
(时间+x+y+z)
根据时间排序,之后把询问拆开,cdq分治去掉x
然后y和z直接带修主席树。。。
(其实不用拆成四维偏序,只拆x,就只用拆成两个询问)
我考试时像SB一样把一个询问拆成了八个然后T到飞起
带修主席树
(似乎二维线段树的常数太大了)
实际跟主席树没有太大关系,就是树状数组套线段树
(没有像主席树那样共用节点)
具体来说,普通主席树一棵树维护的是一个前缀,所以如果要修改就要
O(nlog)
O
(
n
log
)
然而带修主席树用树状数组的思想,每棵树维护的是树状数组中对应的一个区间
所以一次只用修改
O(log)
O
(
log
)
棵线段树,所以兹磁
O(log2)
O
(
log
2
)
修改&查询
没了。
code
不算特别难写。。。
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
using namespace std;
struct AA{
int type,x,y,y2,z,z2,s,id,T;
}a[200001],A[200001],b[100001];
int n,i,j,k,l,Q,x,y,z,r,len,N,sum,sum2,Len1,Len2,X;
char S[10];
int ans[100001];
int root[200001];
int tr[12225000][3];
struct C{
int x,y,z;
}c[1600001];
void New(int t,int x)
{
if (!tr[t][x])
tr[t][x]=++N;
}
inline bool cmp(AA a,AA b)
{
return a.x>b.x || a.x==b.x && a.T<b.T;
}
inline bool cmp1(C a,C b)
{
return a.x<b.x;
}
void change(int t,int l,int r,int x,int s)
{
int mid=(l+r)/2;
tr[t][2]+=s;
if (l==r) return;
if (x<=mid)
{
New(t,0);
change(tr[t][0],l,mid,x,s);
}
else
{
New(t,1);
change(tr[t][1],mid+1,r,x,s);
}
}
void find(int t,int l,int r,int x,int y)
{
int mid=(l+r)/2;
if (x<=l && r<=y)
{
sum+=tr[t][2];
return;
}
if (x<=mid && tr[t][0])
find(tr[t][0],l,mid,x,y);
if (mid<y && tr[t][1])
find(tr[t][1],mid+1,r,x,y);
}
int low(int x)
{
return x&(-x);
}
void dg(int l,int r)
{
int i,j,k,mid=(l+r)/2;
if (l<r)
{
dg(l,mid);
dg(mid+1,r);
}
i=0; //¹é²¢ÅÅÐò
j=l;
k=mid+1;
while (j<=mid || k<=r)
{
i++;
if (k>r || (j<=mid && (a[j].x>a[k].x || a[j].x==a[k].x && a[j].T<a[k].T)))
A[i]=a[j++];
else
A[i]=a[k++];
}
fo(i,1,r-l+1)
a[i+l-1]=A[i];
// sort(a+l,a+r+1,cmp);
X++;
N=Len1;
fo(i,l,r)
if (!a[i].type)
{
if (a[i].id<=mid)
{
j=a[i].y;
while (j<=Len1)
{
// if (!root[j])
// root[j]=++N;
// change(root[j],1,Len2,a[i].z,a[i].s);
if (root[j]!=X)
{
root[j]=X;
tr[j][0]=0;
tr[j][1]=0;
tr[j][2]=0;
}
change(j,1,Len2,a[i].z,a[i].s);
j+=low(j);
}
}
}
else
{
if (a[i].id>mid)
{
sum=0;
j=a[i].y-1;
while (j)
{
// if (root[j])
// find(root[j],1,Len2,a[i].z,Len2);
if (root[j]==X)
find(j,1,Len2,a[i].z,a[i].z2);
j-=low(j);
}
sum2=sum;
sum=0;
j=a[i].y2;
while (j)
{
// if (root[j])
// find(root[j],1,Len2,a[i].z,Len2);
if (root[j]==X)
find(j,1,Len2,a[i].z,a[i].z2);
j-=low(j);
}
sum-=sum2;
ans[a[i].s]+=sum*a[i].type;
}
}
fo(i,Len1+1,N)
{
tr[i][0]=0;
tr[i][1]=0;
tr[i][2]=0;
}
// fo(i,l,r)
// if (!a[i].type && a[i].id<=mid)
// {
// j=a[i].y;
// while (j<=Len1)
// {
// if (root[j])
// root[j]=0;
// else break;
// j+=low(j);
// }
// }
}
void add(int x,int y,int y2,int z,int z2,int type,int s,int id,int T)
{
len++;
a[len].x=x;
a[len].y=y;
a[len].y2=y2;
a[len].z=z;
a[len].z2=z2;
a[len].type=type;
a[len].s=s;
a[len].id=len;
a[len].T=T;
}
int main()
{
// freopen("theresa14.in","r",stdin);
scanf("%d",&n);
fo(i,1,n)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
a[i].type=0;
a[i].s=1;
a[i].id=i;
}
len=n;
j=0;k=0;
for (scanf("%d",&Q); Q; Q--)
{
scanf("%s",S);
if (S[0]=='A')
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,0,z,0,0,1,len,0);
j++;
b[j].x=x;
b[j].y=y;
b[j].z=z;
}
else
if (S[0]=='Q')
{
k++;
scanf("%d%d%d%d",&x,&y,&z,&r);
add(x,y,y+r,z,z+r,1,k,len,1);
add(x+r+1,y,y+r,z,z+r,-1,k,len,1);
}
else
if (S[0]=='C')
{
add(b[j].x,b[j].y,0,b[j].z,0,0,-1,len,0);
j--;
}
}
fo(i,1,len)
{
c[i].x=a[i].y;
c[i].y=i;
c[i].z=1;
}
fo(i,1,len)
{
c[i+len].x=a[i].y2;
c[i+len].y=i;
c[i+len].z=2;
}
sort(c+1,c+len+len+1,cmp1);
j=0;
fo(i,1,len+len)
{
if (i==1 || c[i].x!=c[i-1].x)
j++;
if (c[i].z==1)
a[c[i].y].y=j;
else
a[c[i].y].y2=j;
}
Len1=j;
fo(i,1,len)
{
c[i].x=a[i].z;
c[i].y=i;
c[i].z=1;
}
fo(i,1,len)
{
c[i+len].x=a[i].z2;
c[i+len].y=i;
c[i+len].z=2;
}
sort(c+1,c+len+len+1,cmp1);
j=0;
fo(i,1,len+len)
{
if (i==1 || c[i].x!=c[i-1].x)
j++;
if (c[i].z==1)
a[c[i].y].z=j;
else
a[c[i].y].z2=j;
}
Len2=j;
// fo(i,1,len) cout<<a[i].x<<" "<<a[i].y<<" "<<a[i].y2<<" "<<a[i].z<<" "<<a[i].z2<<" "<<a[i].type<<" "<<a[i].s<<" "<<a[i].id<<" "<<a[i].T<<endl;
X=0;
dg(1,len);
fo(i,1,k)
printf("%d\n",ans[i]);
}
注意事项
本题出题人成心卡时间,所以有几点可供参考
①cdq时用归并排序O(n)合并
②每棵线段树的根先开好,之后记录下时间,就不用全部一起清空了
③O2+register+读入输出优化+看脸
其它
据说一氧化二氢有剧毒(滑稽)