题目描述:戳这里
题解:
这是一道CDQ分治的模板。
我们想要求三维的“正序对”的数量。那么可以通过排序第一维,然后对后两位使用一些数据结构来维护。
但是这样很难维护,而CDQ分治就是解决这样的问题的一个得力工具。
CDQ的思想大概就和归并排序一样。
我们先以x,y,z分别为第1,2,3关键字排序。
考虑分治,对于一段区间,我们把它分成l ~ mid和mid+1 ~ r两段,使其分别按y排序。那么对于这段区间,就形成了左段的x都小于右段的x,并且两边的y都递增的情况。
我们接下来考虑mid+1 ~ r对于 l ~ mid的影响。枚举l ~ mid的元素p,mid+1 ~ r中对当前点的影响就是其中y小于p的y,并且z小于p的z的元素的个数。那么由于y递增,z可以用树状数组来维护。
然后我们其实是想同时做到统计和归并排序,所以只要一边归并,一边统计就好了。
还有一个小细节,就是如果两个数完全相同,那么只能统计到一次(也就是排序后在一堆相同的数最前面的一个),此时要再累加一下,求出后面的数的正确答案。
代码如下:
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005;
int T,n,ans[maxn];
struct node{
int x,y,z,id;
bool operator <(const node &b) const{
return x>b.x||(x==b.x&&y>b.y)||(x==b.x&&y==b.y&&z>b.z);
}
bool operator !=(const node &b) const{
return x!=b.x||y!=b.y||z!=b.z;
}
}a[maxn],c[maxn];
namespace segt{
int tree[maxn];
int lowbit(int x){return x&(-x);}
void upd(int x,int num){for (int i=x;i<=100000;i+=lowbit(i)) tree[i]+=num;}
int que(int x){
int ret=0; for (int i=x;i;i-=lowbit(i)) ret+=tree[i];
return ret;
}
}
void solve(int l,int r){
if (l>=r) return;
int mid=(l+r)>>1;
solve(l,mid); solve(mid+1,r);
for (int i=l;i<=r;i++) c[i]=a[i];
for (int i=mid+1;i<=r;i++) segt::upd(c[i].z,1);
int i=l,j=mid+1;
for (int k=l;k<=r;k++)
if (i<=mid&&(j>r||c[i].y>=c[j].y)) ans[c[i].id]+=segt::que(c[i].z),a[k]=c[i],i++;
else a[k]=c[j],segt::upd(c[j].z,-1),j++;
}
int main(){
scanf("%d",&T);
while (T--){
memset(ans,0,sizeof(ans));
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z),a[i].id=i;
sort(a+1,a+1+n);
solve(1,n);
int sum=0;
for (int i=1;i<=n;i++)
if (a[i]!=a[i-1]) sum=1;
else ans[a[i].id]+=sum,sum++;
for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
return 0;
}