BZOJ3262 陌上花开(CDQ分治+树状数组)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20180602_csq/article/details/102252655

3262: 陌上花开

Description

有n朵花,每朵花有三个属性:花形(s)、颜色(c)、气味(m),用三个整数表示。

现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。

定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。

显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。

Input

第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。

以下N行,每行三个整数si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的属性

Output

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

Sample Input

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

Sample Output

3
1
3
0
1
0
1
0
0
1

题解

注意:一朵花的评级=所有属性均小于等于它的花的数量

由于要考虑等于的情况,所以

2 3

1 2 3

1 2 3

的答案是

0

2

我们可以先考虑把所有属性均相同的花去重,就看成一种花,记录一下该种花的出现次数

然后将所有花以第一个属性为关键字来排序

就可以将问题转化为经典的二维数点问题啦

然后直接用CDQ分治+树状数组就A了

注意细节的处理

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0;
	while((c=getchar())<'0'||c>'9');
	do{num=num*10+c-48;c=getchar();}while(c>='0'&&c<='9');
	return num;
}
int n,K;
#define N 100005
struct anode{
	int x,y,z;
	bool operator == (const anode &t)const{
		return (x==t.x)&&(y==t.y)&&(z==t.z);
	}
}a[N];
bool cmp(anode q,anode w){return q.x<w.x||(q.x==w.x&&q.y<w.y)||(q.x==w.x&&q.y==w.y&&q.z<w.z);}
int treearray[2*N];
void update(int x,int k)
{
	while(x<=K){
		treearray[x]+=k;
		x+=(x&-x);
	}
}
int getsum(int x)
{
	int ans=0;
	while(x){
		ans+=treearray[x];
		x-=(x&-x);
	}
	return ans;
}
struct node{
	int op,t,x,y,ct,ans;
	node(){}
	node(int a,int b,int c,int d,int e,int f){op=a;t=b;x=c;y=d;ct=e;ans=f;}
}q[5*N],nq[5*N];
int ans[N],con[N],cnt;
bool vis[N];
bool cmp2(node s,node t){return s.x<t.x||(s.x==t.x&&s.y<t.y)||(s.x==t.x&&s.y==t.y&&s.t<t.t);}
void CDQ(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1,i,cnt1=l-1,cnt2=mid;
	for(i=l;i<=r;i++){
		if(q[i].t<=mid&&q[i].op==1) update(q[i].y,q[i].ct);
		if(q[i].t>mid&&q[i].op==2) q[i].ans+=getsum(q[i].y);
	}
	for(i=l;i<=r;i++){
		if(q[i].t<=mid&&q[i].op==1) update(q[i].y,-q[i].ct);
		if(q[i].t<=mid)nq[++cnt1]=q[i];
		else nq[++cnt2]=q[i];
	}
	for(i=l;i<=r;i++)q[i]=nq[i];
	CDQ(l,mid);CDQ(mid+1,r);
}
int main()
{
	int i;
	n=gi();K=gi();
	for(i=1;i<=n;i++){a[i].x=gi();a[i].y=gi();a[i].z=gi();con[i]=1;}
	sort(a+1,a+n+1,cmp);
	for(i=2;i<=n;i++){
		if(a[i]==a[i-1]){
			vis[i-1]=1;
			con[i]+=con[i-1];
		}
	}
	for(i=1;i<=n;i++){
		if(!vis[i]){
			q[++cnt]=node(2,cnt,0,0,con[i],0);
			q[++cnt]=node(2,cnt,a[i].y,0,con[i],0);
			q[++cnt]=node(2,cnt,0,a[i].z,con[i],0);
			q[++cnt]=node(2,cnt,a[i].y,a[i].z,con[i],0);
			q[++cnt]=node(1,cnt,a[i].y,a[i].z,con[i],0);
		}
	}
	sort(q+1,q+cnt+1,cmp2);
	CDQ(1,cnt);
	for(i=1;i<=cnt;i++)
		if(q[i].op==2){
			ans[q[i].ans-q[i+2].ans+q[i+3].ans-q[i+1].ans+q[i].ct-1]+=q[i].ct;
			i+=3;
		}
	for(i=0;i<n;i++)
		printf("%d\n",ans[i]);
}

其实,个人感觉CDQ分治是先将第一维排好序

然后计算前一半对后一半的贡献

再将序列按照第二维分为两个序列(此时这两个序列内部各满足第一维有序,两个序列之间是第二维有序、第一维无序的)

然后继续分治下去

这个序列就会一点点地从第一维有序变成第二维有序,在这个过程中你就可以统计答案了

猜你喜欢

转载自blog.csdn.net/C20180602_csq/article/details/102252655