题目要求我们计算这样一个东西:
一个序列有
n
n
n个元素,每个元素有三个权值
a
i
,
b
i
,
c
i
.
a_i,b_i,c_i.
ai,bi,ci. 对于每个权值,需要求出有多少个
j
j
j,满足
a
j
≤
a
i
,
b
j
≤
b
i
,
c
j
≤
c
i
.
a_j\leq a_i,b_j\leq b_i,c_j\leq c_i.
aj≤ai,bj≤bi,cj≤ci. ——三维偏序
数据范围:
n
<
=
1
0
5
,
a
i
,
b
i
,
c
i
<
=
2
∗
1
0
5
n<=10^5, a_i,b_i,c_i<=2*10^5
n<=105,ai,bi,ci<=2∗105
首先来看一维偏序:
逆序对?记录下原来的位置
p
o
s
pos
pos,然后直接
s
o
r
t
sort
sort一遍……
二维偏序:
发现直接
s
o
r
t
sort
sort一遍不行……
先以
a
a
a为第一关键字,以
b
b
b为第二关键字,从小到大
s
o
r
t
sort
sort一遍。
然后从左到右扫过去,对于第
i
i
i个元素,统计
[
1
,
i
−
1
]
[1,i-1]
[1,i−1]中有多少个元素的
b
b
b比它的
b
b
b小,用树状数组维护即可。
三维偏序:
发现好像没法用
s
o
r
t
sort
sort+树状数组大力求了……
用树状数组套动态开点线段树或者树状数组套平衡树做。 ——巨神mhy
给巨神 m h y mhy mhy的博客打个广告qwq:前往膜拜julao
然而我并不想写树套树……然后就学了一下
C
D
Q
CDQ
CDQ分治……发现常数蜃小qwq
C
D
Q
CDQ
CDQ分治思路:
先
s
o
r
t
sort
sort一遍,然后问题转化成一个
n
n
n个元素的序列,对于每个元素,有多少个
j
j
j,满足
j
≤
i
,
b
j
≤
b
i
,
c
j
≤
c
i
.
j\leq i , b_j\leq b_i , c_j\leq c_i.
j≤i,bj≤bi,cj≤ci.
因为是cdq分治,所以要分治qwq
把假设当前序列为
[
l
,
r
]
[l,r]
[l,r]
(
l
<
r
)
(l<r)
(l<r),那么序列分为
[
l
,
m
i
d
]
[l,mid]
[l,mid]和
[
m
i
d
+
1
,
r
]
[mid+1,r]
[mid+1,r]。
首先递归求解两个子序列,然后考虑合并。
把两个子序列分别以
b
b
b为第一关键字,以
c
c
c为第二关键字从小到大排序。
以下面这个序列为例:
枚举右边的子序列的每一个元素,假设当前枚举到的元素编号为
i
i
i。
因为右边子序列内部对
i
i
i的影响已经递归求解好,所以只用考虑
[
l
,
m
i
d
]
[l,mid]
[l,mid]中的元素对它的贡献。
假设当编号为
i
i
i时左边子序列的元素
b
b
b值小于它的
b
b
b值的的区间是
[
l
,
p
r
e
p
t
r
]
.
[l,preptr].
[l,preptr].
当编号枚举到
i
+
1
i+1
i+1时左边子序列元素
b
b
b值小于它的区间是
[
l
,
n
o
w
p
t
r
]
[l,nowptr]
[l,nowptr].
因为排序后,
b
b
b值单调不减,所以
b
[
i
+
1
]
>
=
b
[
i
]
.
b[i+1]>=b[i].
b[i+1]>=b[i]. 故
b
[
n
o
w
p
t
r
]
>
=
b
[
p
r
e
p
t
r
]
,
b[nowptr]>=b[preptr],
b[nowptr]>=b[preptr],即
n
o
w
p
t
r
>
=
p
r
e
p
t
r
.
nowptr>=preptr.
nowptr>=preptr.
所以
p
t
r
ptr
ptr指针是不减的qwq。
所以大力扫过去,左边维护一个不减的指针 p t r ptr ptr,在 [ l , p t r ] [l,ptr] [l,ptr]中用树状数组维护有多少个元素 c c c值比当前元素 c c c值小即可。
模拟一下上面那张图:
i
=
4
i=4
i=4 ,此时
b
i
=
3
,
c
i
=
2
b_i=3,c_i=2
bi=3,ci=2
此时
p
t
r
=
1
ptr=1
ptr=1,把
c
1
=
4
c_1=4
c1=4加入树状数组。
然后查询树状数组中有多少个
<
=
2
<=2
<=2,显然是
0.
0.
0. 把
0
0
0加到
a
n
s
[
4
]
ans[4]
ans[4]中。
i
=
5
i=5
i=5 ,此时
b
i
=
5
,
c
i
=
5
b_i=5,c_i=5
bi=5,ci=5
此时
p
t
r
=
2
ptr=2
ptr=2,把
c
2
=
3
c_2=3
c2=3加入树状数组。
然后查询树状数组中有多少个
<
=
5
<=5
<=5,显然是
2.
2.
2. 把
2
2
2加到
a
n
s
[
5
]
ans[5]
ans[5]中。
i
=
6
i=6
i=6 ,此时
b
i
=
6
,
c
i
=
3.
b_i=6,c_i=3.
bi=6,ci=3.
此时
p
t
r
=
2
ptr=2
ptr=2,不把任何数加入树状数组。
然后查询有多少个
<
=
3
<=3
<=3,显然是
1.
1.
1. 把
1
1
1加到
a
n
s
[
6
]
ans[6]
ans[6]中。
时间复杂度:
注:我也不确定对不对qwq
由
T
(
n
)
=
2
T
(
n
2
)
+
O
(
n
l
o
g
k
)
\large T(n)=2T(\frac{n}{2})+O(nlogk)
T(n)=2T(2n)+O(nlogk)(好像是这样?)
得
T
(
n
)
=
O
(
n
l
o
g
n
l
o
g
k
)
T(n)=O(nlognlogk)
T(n)=O(nlognlogk) (大概是这样了qwq)
就没了吗?
n
a
i
v
e
!
naive!
naive!
要注意两点:
1.
1.
1.发现当
a
i
=
a
j
,
b
i
=
b
j
,
c
i
=
c
j
a_i=a_j,b_i=b_j,c_i=c_j
ai=aj,bi=bj,ci=cj的时候,会出一些奇奇怪怪的问题。
所以要再记录一个
v
i
s
i
vis_i
visi,表示三元组
(
a
i
,
b
i
,
c
i
)
(a_i,b_i,c_i)
(ai,bi,ci)出现过多少次。
然后最后
(
a
i
,
b
i
,
c
i
)
(a_i,b_i,c_i)
(ai,bi,ci)的答案还要加上
v
i
s
i
−
1
vis_i-1
visi−1。
2. 2. 2.每次分治完,要把树状数组清空
毒瘤代码
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<math.h>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
namespace I_Love {
const int Size=200005;
const int LOG=20;
int n,k,ans[Size],out[Size];
struct Flower {
int id,a,b,c,vis,ans;
} tmp[Size],w[Size];
//int vis[Size];
inline bool compa(Flower x,Flower y) {
if(x.a!=y.a) return x.a<y.a;
if(x.b!=y.b) return x.b<y.b;
return x.c<y.c;
}
int tree[Size];
inline int lowbit(int x) {
return x&(-x);
}
inline void update(int x,int val) {
for(re i=x; i<=k; i+=lowbit(i)) {
tree[i]+=val;
}
}
inline int query(int x) {
int tot=0;
for(re i=x; i; i-=lowbit(i)) {
tot+=tree[i];
}
return tot;
}
inline bool compb(Flower x,Flower y) {
if(x.b!=y.b) return x.b<y.b;
return x.c<y.c;
}
void CDQ_Divide(int l,int r) {
if(l==r) return;
int mid=(l+r)>>1;
CDQ_Divide(l,mid);
CDQ_Divide(mid+1,r);
sort(w+l,w+1+mid,compb);
sort(w+1+mid,w+1+r,compb);
int ptr=l-1;
for(re i=mid+1; i<=r; i++) {
while(ptr<mid && w[ptr+1].b<=w[i].b) {
ptr++;
update(w[ptr].c,w[ptr].vis);
}
w[i].ans+=query(w[i].c);
}
for(re i=l; i<=ptr; i++) {
update(w[i].c,-w[i].vis);
}
}
void Fujibayashi_Ryou() {
// freopen("data.txt","r",stdin);
// freopen("WA.txt","w",stdout);
n=read();
k=read();
for(re i=1; i<=n; i++) {
tmp[i].a=read();
tmp[i].b=read();
tmp[i].c=read();
tmp[i].id=i;
}
sort(tmp+1,tmp+1+n,compa);
int cnt=1;
w[1]=tmp[1]; w[1].vis=1;
//去重
for(re i=2; i<=n; i++) {
if(tmp[i].a!=tmp[i-1].a || tmp[i].b!=tmp[i-1].b || tmp[i].c!=tmp[i-1].c) {
w[++cnt]=tmp[i];
w[cnt].vis=1;
} else {
w[cnt].vis++;
}
}
CDQ_Divide(1,cnt);
sort(w+1,w+1+cnt,compa);
for(re i=1; i<=cnt; i++) {
// printf("%d %d %d %d\n",w[i].a,w[i].b,w[i].c,w[i].ans+w[i].vis-1);
out[w[i].ans+w[i].vis-1]+=w[i].vis;
}
for(re i=0; i<n; i++) {
printf("%d\n",out[i]);
}
}
}
int main() {
I_Love::Fujibayashi_Ryou();
return 0;
}
/*
10 3
1 2 2
2 2 1
2 2 3
2 2 3
2 3 3
3 2 1
3 2 1
3 2 1
3 2 2
3 3 3
*/