[BZOJ3262]:陌上花开(CDQ分治)

题目传送门


题目描述

有$n$朵花,每朵花有三个属性:花形$(s)$、颜色$(c)$、气味$(m)$,用三个整数表示。
现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。
定义一朵花$A$比另一朵花$B$要美丽,当且仅$S_a\geqslant S_b,C_a\geqslant C_b,M_a\geqslant M_b$。
显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。


输入格式

第一行为$N,K$,分别表示花的数量和最大属性值。
以下$N$行,每行三个整数$s_i,c_i,m_i$,表示第$i$朵花的属性。


输出格式

包含$N$行,分别表示评级为$0...N-1$的每级花的数量。


样例

样例输入

10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1

样例输出

3
1
3
0
1
0
1
0
0
1


数据范围与提示

$1\leqslant N\leqslant 100,000$。

$1\leqslant K\leqslant 200,000$。

$1\leqslant s_i,c_i,m_i\leqslant K$。


题解

这道题好像可以用$bitset$,$CDQ$分治,$K-Dtree$解决。

$bitset$好像跑不过,我也不清楚……

最尴尬的是$K-Dtree$我不会……

那么我就来讲讲$CDQ$分治。

假设我们不考虑$m_i$,那么我们就变成了二元组排序,可以先将$s_i$排序,然后问题就转化成了:在一个数列里,找在$i$之前有几个数小于$c_i$。

很显然要使用树状数组维护即可在$\Theta (n\log n)$时间内求出答案。

那么现在又加了一维,怎么办呢?

首先,将这个三元组排序并去重,那么权值$a$已经随下标有序。

然后我们就需要引入$CDQ$分治,假设当前处理的区间为$[L,R]$其中点为$M$,那么我们在处理当前区间的时候已经保证了$[L,M]$和$[M+1,R]$的有序,因为$[L,M]$中的$s_i$一定小于等于$[M+1,R]$中的$s_i$,所以我们只需要维护一个单调指针,按照$[L,M]$中的$c_i$的大小从小到大将其放入树状数组,并在其中查询前缀和即可。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec
{
	int s;
	int c;
	int m;
	int x;
	int id;
}e[2000001];
int n,k;
int tr[8000001];
int ans[2000001],q[2000001],b[2000001];
bool cmp1(rec a,rec b){return a.s<b.s||(a.s==b.s&&a.c<b.c)||(a.s==b.s&&a.c==b.c&&a.m<b.m);}
bool cmp2(rec a,rec b){return a.c<b.c||(a.c==b.c&&a.m<b.m)||(a.c==b.c&&a.m==b.m&&a.x<b.x);}
int lowbit(int x){return x&-x;}
void add(int x,int y)
{
	while(x<=k)
	{
		tr[x]+=y;
		x+=lowbit(x);
	}
}
int query(int x)
{
	int res=0;
	while(x)
	{
		res+=tr[x];
		x-=lowbit(x);
	}
	return res;
}
void cdq(int l,int r)//cdq分治
{
	if(l==r)return;
	int mid=(l+r)>>1;
	cdq(l,mid);
	cdq(mid+1,r);
	sort(e+l,e+r+1,cmp2);
	for(int i=l;i<=r;i++)
	if(e[i].x<=mid)add(e[i].m,1);
	else q[e[i].id]+=query(e[i].m);
	for(int i=l;i<=r;i++)
	if(e[i].x<=mid)add(e[i].m,-1);
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&e[i].s,&e[i].c,&e[i].m);
		e[i].id=i;
	}
	sort(e+1,e+n+1,cmp1);
	for(int i=1;i<=n;)//手动去重
	{
		int flag=i+1;
		while(flag<=n&&e[i].s==e[flag].s&&e[i].c==e[flag].c&&e[i].m==e[flag].m)flag++;
		flag--;
		while(i<=flag)
		{
			b[e[i].id]=e[flag].id;
			i++;
		}
	}
	for(int i=1;i<=n;i++)e[i].x=i;
	cdq(1,n);
	for(int i=1;i<=n;i++)
		ans[q[b[e[i].id]]]++;
	for(int i=0;i<n;i++)
		printf("%d\n",ans[i]);
	return 0;
}

rp++

posted @ 2019-07-26 21:40  HEOI-动动  阅读(91)  评论(0编辑  收藏  举报