思想
CDQ 分治的操作思想是消除维度。
思路
比如这道题,每个元素是一个三元组。
由于是无序的,所以我们可以考虑对 a a a 升序排序,这样以后进行其他操作时, a a a 已经是有序的,不需要再考虑。
而为了保证后续的 b b b 都是有序的,又不改变现在 a a a 的有序,所以可以考虑以 b b b 为基准进行归并排序。
回顾一下归并排序的过程:将数组分为左右两部分,先左右依次排序,再合并。
也就是说,在合并之前,左边的所有 a a a 一定小于等于右边的所有 a a a,因为原先的 a a a 都是有序的(这一点要想清楚)。
接着,我们在合并的同时遍历 b r b_r br 找到左边所有小于它的 b l b_l bl,由于归并排序, a a a 一定都满足条件。
现在已经消除了两个维度了,先算一下时间复杂度。
-
排序: O ( n log n ) O(n \log n) O(nlogn)
-
归并排序: O ( n log n ) O(n \log n) O(nlogn)
排序是为了归并排序的 a a a 合法打下基础,归并排序保证了 a , b a,b a,b 合法, c c c 合法需要建立在 a , b a,b a,b 合法的基础上,所以应该套在归并排序里面。
那么这个实现需要达到什么?
- c c c 小于当前 c n o w c_{now} cnow 的元素,也就是说查找 c c c 的值为 1 ∼ c n o w − 1 1\sim c_{now}-1 1∼cnow−1 的元素个数。
- 可以实现单点的修改,增减每个值对应的元素个数。
- 由于需要套在归并排序里面,所以时间复杂度需要做到 log \log log 级别。
那么满足这几个条件的东西有什么?很明显:树状数组。
树状数组有一个特性,点 x x x 覆盖的不是一个点,一个 1 ∼ x 1\sim x 1∼x 的区间,所以 c c c 小于当前 c n o w c_{now} cnow 的元素就是 q u e r y ( c ) query(c) query(c)
实现
虽然说思路有了,但是实现起来细节上可能还会有点问题。
1. 去重
题目中没有保证元素不相同,所以进行去重,同时统计个数(两个完全相同的元素可以互相大于)。
n=read(),k=read();
for(int i=1;i<=n;i++){
a[i].a=read(),a[i].b=read(),a[i].c=read();
a[i].cnt=1;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
if(a[i].a==a[m].a&&a[i].b==a[m].b&&a[i].c==a[m].c)a[m].cnt++;
else a[++m]=a[i];
}
2. 树状数组的修改
合并和修改可以同时进行,记得是加上 a [ i ] . c n t a[i].cnt a[i].cnt 而不是 1 1 1,完事了记得清空。
while(j<=r){
while(i<=mid&&a[i].b<=a[j].b){
update(a[i].c,a[i].cnt);
b[q++]=a[i++];
}
a[j].ans+=query(a[j].c);
b[q++]=a[j++];
}
for(int p=l;p<i;p++)update(a[p].c,-a[p].cnt);
3. 后面忘了
后面没了。
代码
#include<bits/stdc++.h>
#define lowbit(x) x&-x
#define endl putchar('\n')
using namespace std;
const int N=1e6+5;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
int n,m,k;
struct node{
int a,b,c,cnt,ans;
}a[N],t[N],b[N];
int ans[N];
int bit[N];
bool cmp(node a,node b){
return a.a<b.a||(a.a==b.a&&a.b<b.b)||(a.a==b.a&&a.b==b.b&&a.c<b.c);
}
void update(int x,int v){
while(x<=k){
bit[x]+=v;
x+=lowbit(x);
}
}
int query(int x){
int res=0;
while(x){
res+=bit[x];
x-=lowbit(x);
}
return res;
}
void cdq(int l,int r){
if(l==r)return;
int mid=l+r>>1;
cdq(l,mid);
cdq(mid+1,r);
int i=l,j=mid+1,q=l;
while(j<=r){
while(i<=mid&&a[i].b<=a[j].b){
update(a[i].c,a[i].cnt);
b[q++]=a[i++];
}
a[j].ans+=query(a[j].c);
b[q++]=a[j++];
}
for(int p=l;p<i;p++)update(a[p].c,-a[p].cnt);
while(i<=mid)b[q++]=a[i++];
for(int p=l;p<=r;p++)a[p]=b[p];
}
signed main(){
n=read(),k=read();
for(int i=1;i<=n;i++){
a[i].a=read(),a[i].b=read(),a[i].c=read();
a[i].cnt=1;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
if(a[i].a==a[m].a&&a[i].b==a[m].b&&a[i].c==a[m].c)a[m].cnt++;
else a[++m]=a[i];
}
cdq(1,m);
for(int i=1;i<=m;i++)ans[a[i].ans+a[i].cnt-1]+=a[i].cnt;
for(int i=0;i<n;i++)print(ans[i]),endl;
}
468

被折叠的 条评论
为什么被折叠?



