【bzoj3262】陌上花开
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朵花的属性
以下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
Hint
1 <= N <= 100,000, 1 <= K <= 200,000
Solution
这是一道非常经典的CDQ,好像有一个更为笼统的说法是叫做三维偏序。。。
我们大概的处理方式就是将第一个属性排序之后作为时间轴来添加时间,而每一朵花剩下的两个属性都看作为平面上的点坐标,那么我们要求的就是每一个点左下角的点的个数了。将拥有相同个数的点数累计后就得到了输出的答案。
CODE
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int Maxn=100005;
const int Maxk=200005;
inline int read(){
char c;int rec=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
return rec;
}
int n,m,low[Maxk],ans[Maxn];
struct Flower{int a,b,c,cnt,ans;}a[Maxn],p[Maxn];
inline bool cmp1(Flower x,Flower y){return x.a<y.a||x.a==y.a&&x.b<y.b||x.a==y.a&&x.b==y.b&&x.c<y.c;}
inline bool cmp2(Flower x,Flower y){return x.b<y.b||x.b==y.b&&x.c<y.c;}
//分别是按照时间轴的大致排序和在平面上的点对排序
inline void Add(int x,int d){while(x<=m)low[x]+=d,x+=x&-x;return ;}
inline int Ask(int x){int rec=0;while(x)rec+=low[x],x-=x&-x;return rec;}
//水水的树状数组
inline void CDQ(int L,int R){
if(L==R)return;
int mid=(L+R)>>1;
CDQ(L,mid);CDQ(mid+1,R);//可以试一下先分治
sort(p+L,p+mid+1,cmp2);
sort(p+mid+1,p+R+1,cmp2);//将被处理的连个区间进行点对排序
int i=L,j=mid+1;
while(j<=R){
while(i<=mid&&p[i].b<=p[j].b){Add(p[i].c,p[i].cnt);i++;}
p[j].ans+=Ask(p[j].c);j++;
}for(j=L;j<i;j++)Add(p[j].c,-p[j].cnt);
return ;
}
int main(){
int N=read();m=read();
for(int i=1;i<=N;i++)a[i].a=read(),a[i].b=read(),a[i].c=read();
sort(a+1,a+N+1,cmp1);int cnt=0;
for(int i=1;i<=N;i++){cnt++;
if(a[i].a!=a[i+1].a||a[i].b!=a[i+1].b||a[i].c!=a[i+1].c)
p[++n]=a[i],p[n].cnt=cnt,cnt=0;
}CDQ(1,n);
for(int i=1;i<=n;i++)ans[p[i].ans+p[i].cnt-1]+=p[i].cnt;//累加得出某等级的花的个数
for(int i=0;i<N;i++)cout<<ans[i]<<'\n';
return 0;
}