P3810 【模板】三维偏序(陌上花开)

闲扯

咕了不知道多久的 $CDQ$ 分治,今天终于初步学了一下。。

题面

P3810 【模板】三维偏序(陌上花开)

Solution

$CDQ$ 能解决的最基础的问题是二维偏序问题,例如树状数组的单点加和区间求和。

当问题拓展到三维时,我们需要先对其中一维排一下序,这样问题就剩下两维了。

但是我们能处理的只剩一维,还有一维怎么办?

这时候就需要用其他的东西来处理剩下的这一维,列如树状数组或者再套一层 $CDQ$

对于这题,我们先将序列按照 $a$ 从小到大排一次序,这样我们只需要处理 $b,c$ 这两维。

进行 $CDQ$ 分治时,我们先递归处理左右子区间,然后统计左区间对右区间的贡献。

我们先将两个子区间按照先 $b$ 后 $c$ 的顺序排个序,然后依次枚举右区间的每一个数。将左区间里面所有还未加入且 $b_i<=b_j$ 的贡献加入树状数组(里面维护的是 $c$ 小于 $c_k$ 的个数),然后查询当前的所有 $c<c_j$ 的个数。

处理完该区间之后,我们将所有加入了树状数组的数删去,然后进入下一轮求解即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il _print(T x){
if(x/10) _print(x/10);
putchar(x%10+'0');
}
template<class T>il print(T x){
if(x<0) putchar('-'),x=-x;
_print(x);
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x%mod;
while(m){
if(m&1) res=(1ll*res*bas)%mod;
bas=(1ll*bas*bas)%mod,m>>=1;
}
return res%mod;
}
const int MAXN = 1e5+5;
int n,k,cnt,ans[MAXN],val,T[MAXN];
struct Node{
int a,b,c,val,ans;
}a[MAXN],b[MAXN];
inl bool cmp(Node a,Node b){
if(a.a==b.a) return a.b==b.b?a.c<b.c:a.b<b.b;
return a.a<b.a;
}
inl bool cmp1(Node a,Node b){return a.b==b.b?a.c<b.c:a.b<b.b;}
il updata(int pos,int val){for(;pos<=k;pos+=lowbit(pos)) T[pos]+=val;}
it query(int pos){ri res=0;for(;pos;pos-=lowbit(pos)) res+=T[pos];return res;}
il CDQ(int l,int r){
if(l==r) return ;
CDQ(l,mid),CDQ(mid+1,r);
sort(a+l,a+mid+1,cmp1),sort(a+mid+1,a+r+1,cmp1);
ri t1=l,t2=mid+1;
for(;t2<=r;++t2){
while(a[t1].b<=a[t2].b&&t1<=mid) updata(a[t1].c,a[t1].val),++t1;
a[t2].ans+=query(a[t2].c);
}
for(ri i=l;i<t1;++i) updata(a[i].c,-a[i].val);
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n),read(k);
for(ri i=1;i<=n;++i) read(b[i].a),read(b[i].b),read(b[i].c),b[i].val=1;
sort(b+1,b+1+n,cmp);
for(ri i=1;i<=n;++i){
++val;
if(b[i].a==b[i+1].a&&b[i].b==b[i+1].b&&b[i].c==b[i+1].c) continue;
a[++cnt]=b[i],a[cnt].val=val,val=0;
}
CDQ(1,cnt);
for(ri i=1;i<=cnt;++i) ans[a[i].ans+a[i].val-1]+=a[i].val;
for(ri i=0;i<n;++i) print(ans[i]),puts("");
return 0;
}

总结

$CDQ$ 分治听说是个应用十分灵活的算法,只做一道模板题显然不行,需要更多的练习才行。