CDQ分治模板:HDU5618 Jam's problem again

题目描述:戳这里
题解:
这是一道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
#include
#include
#include
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;
}

你可能感兴趣的:(题解,知识整理,POJ,HDU,ZOJ,LOJ,Topcoder题解)