题解-P3810
P3810 【模板】三维偏序(陌上花开)
更好的阅读体验1
更好的阅读体验2
前置算法
- 树状数组求逆序对
- 归并排序求逆序对
解题之前,让我们来看一看弱化版本
题意
给定两个长度为数组
解法1
考虑用结构体把数组存起来,对
还没完。由于可能出现
提到去重,就要在结构体里面多加一个
去重毒瘤的要死
解法2
还是用结构体存,对
回想一下归并排序求逆序对,我们求的是
在这里,
深刻注意解法2
三维偏序
进入正题
第一步
和二维偏序一样,先按
第二步
为了简化题目,先考虑简易版本不存在
进入到今天的正题: CDQ 讲解部分。
CDQ 分治,顾名思义,是一种分治。而分治,就需要把
按分治三部曲走,接下来就是合并左右区间并统计答案了了,这里按归并排序求逆序对的思路来。
由于
在合并中,我们分两种情况:
- 此时的最小值在左,那么我们让树状数组的左边那个数的
c 值位置加一 - 此时的最小值在右,那么我们让答案加上树状数组从
1 到右边数的c 值位置的和
如果搞不懂为什么左是加,右是查,建议重新看一看归并排序求逆序对。
注意:每一次使用完后树状数组要清空。如果单纯 memset,会超时(因为是
int tmp[maxn]; // 临时存放合并好的值的数组
void CDQ(int l,int r){
if(l == r) return;
int mid = l + r >> 1,p = l,q = mid + 1,len = 0;
CDQ(l,mid),CDQ(mid + 1,r); // 递归处理
while(p <= mid && q <= r){ // 合并子区间
if(a[p].b <= a[q].b) bit.update(a[p].c,1),tmp[++len] = a[p++]; // 选左边,此时更新树状数组
else a[q].ans += bit.query(a[q].c),tmp[++len] = a[q++]; // 选右边,此时答案要加上值域树状数组的查询
}
while(p <= mid) bit.update(a[p].c,1),tmp[++len] = a[p++]; // 归并左边剩下部分
while(q <= r) a[q].ans += bit.query(a[q].c),tmp[++len] = a[q++]; // 归并右边剩下部分
for(int i = l;i <= mid;++i) bit.update(a[i].c,-1); // 清空
for(int i = 1;i <= len;++i) a[l + i - 1] = tmp[i]; // 把临时数组里的值拷贝到原数组
}
第三步
由于第二步只能处理不存在相同的情况,接下来讲解如果存在
注意刚才我们树状数组是这样更新的:bit.update(a[p].c,1)
这里的 1 实际上就是
因此代码如下:
int tmp[maxn];
void CDQ(int l,int r){
if(l == r) return;
int mid = l + r >> 1,p = l,q = mid + 1,len = 0;
CDQ(l,mid),CDQ(mid + 1,r);
while(p <= mid && q <= r){
if(a[p].b <= a[q].b) bit.update(a[p].c,a[p].x),tmp[++len] = a[p++];
else a[q].ans += bit.query(a[q].c),tmp[++len] = a[q++];
}
while(p <= mid) bit.update(a[p].c,a[p].x),tmp[++len] = a[p++];
while(q <= r) a[q].ans += bit.query(a[q].c),tmp[++len] = a[q++];
for(int i = l;i <= mid;++i) bit.update(a[i].c,-a[i].x);
for(int i = 1;i <= len;++i) a[l + i - 1] = tmp[i];
}
第四步
这时 CDQ 分治已经完成了,我们现在需要统计答案
按照刚才的代码,a[i].ans 表示 a[i].x 正好表示了 a[i].ans + a[i].x 就是去重后第
for(int i = 1;i <= cnt;++i) res[a[i].ans + a[i].x - 1] += a[i].x; // 注意是 + a[i].x,因为还有与 i 值相同所有 j,其总个数是 a[i].x
for(int i = 0;i < n;++i) printf("%d\n",res[i]);
#include<stdio.h>
#include<algorithm>
const int maxn = 114514;
int n,k;
struct BIT{
int t[maxn << 1];
int lowbit(int i){return i & -i;}
void update(int i,int x){for(;i <= k;i += lowbit(i)) t[i] += x;}
int query(int i){int ans = 0;for(;i;i -= lowbit(i)) ans += t[i];return ans;}
} bit; // 树状数组
struct number{
int a,b,c,ans,x;
bool operator<(const number& y) const{return a != y.a ? a < y.a : b != y.b ? b < y.b : c < y.c;}
} a[maxn],tmp[maxn]; // 数的结构体
int res[maxn];
void CDQ(int l,int r){
if(l == r) return;
int mid = l + r >> 1,p = l,q = mid + 1,len = 0;
CDQ(l,mid),CDQ(mid + 1,r);
while(p <= mid && q <= r){
if(a[p].b <= a[q].b) bit.update(a[p].c,a[p].x),tmp[++len] = a[p++];
else a[q].ans += bit.query(a[q].c),tmp[++len] = a[q++];
}
while(p <= mid) bit.update(a[p].c,a[p].x),tmp[++len] = a[p++];
while(q <= r) a[q].ans += bit.query(a[q].c),tmp[++len] = a[q++];
for(int i = l;i <= mid;++i) bit.update(a[i].c,-a[i].x);
for(int i = 1;i <= len;++i) a[l + i - 1] = tmp[i];
}
int main(){
scanf("%d%d",&n,&k);
for(int i = 1;i <= n;++i) scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),a[i].x = 1,a[i].ans = 0;
std::sort(a + 1,a + n + 1); // 排序
int cnt = 1;
for(int i = 2;i <= n;++i)
if(a[i].a == a[cnt].a && a[i].b == a[cnt].b && a[i].c == a[cnt].c) ++a[cnt].x; // 如果遇到与 i 一样的,x 值就要自加一
else a[++cnt] = a[i];
CDQ(1,cnt); // 注意不是 CDQ(1,n)
for(int i = 1;i <= cnt;++i) res[a[i].ans + a[i].x - 1] += a[i].x;
for(int i = 0;i < n;++i) printf("%d\n",res[i]);
return 0;
}