Description
有n朵花,每朵花有三个属性:花形(s)、颜色©、气味(m),用三个整数表示。现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。定义一朵花A比另一朵花B要美丽,当且仅
S
a
>
=
S
b
S_a>=S_b
Sa>=Sb,
C
a
>
=
C
b
C_a>=C_b
Ca>=Cb,
M
a
>
=
M
b
M_a>=M_b
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
思路
首先按照题目定义,两朵属性完全相同的花都比对方要美丽,所以读入所有花的数据之后有必要排序去重,记录一下每种(三元组)属性的花的数量。
然后没什么想法。。。。。
开始面向题解编程
看了下网上的题解,说是cdq分治套树状数组。
考虑将待处理的序列先按S排序,再将序列一分两半,我们分别计算完了这两半的答案,那么剩下的需要计算的答案就是左边对右边的贡献(考虑先前是按S排序故一定满足左边的S小于或等于右边的S)
这时我们分别对左边一半和右边一半按C排序,再分别建立两个指针用于遍历左边一半和右边一半。
现在左边一半和右边一半都满足按C升序排列并且左边的S均小于等于右边的S。
我们考虑建立一个树状数组用来计数。sum(n)用于树状数组求和。
- 左边当前位置的C大于右边当前位置的C:对于右边当前位置,答案加上sum(右边当前位置对应的M值),右边指针+1
- 左边当前位置的C小于等于右边当前位置的C:对于左边当前位置,在树状数组中插入左边当前位置对应的M值,左边指针+1
当遍历完右边序列后,清空树状数组。
AC代码
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef struct data{
int s,c,m,num,ans;
}data;
int cmp(data a,data 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);
}
data d[100005];
int su[200005],ans[200005],n,k;
int N=0;
int lowbit(int x)
{
return (x)&(-x);
}
void inse(int now,int v)
{
while(now<=k)
{
su[now]+=v;
now+=lowbit(now);
}
return;
}
int sum(int x)
{
int ans=0;
while(x)
{
ans+=su[x];x-=lowbit(x);
}
return ans;
}
int cmp2(data a,data b)
{
return a.c<b.c;
}
void cdq(int l,int r)
{
if(l==r)return;
int mid=(l+r)/2;
cdq(l,mid);cdq(mid+1,r);
sort(d+l,d+mid+1,cmp2);
sort(d+mid+1,d+r+1,cmp2);
int h1=l,h2=mid;
do
{
h2++;
while(h1<=mid&&d[h1].c<=d[h2].c)
{
inse(d[h1].m,d[h1].num+1);h1++;
}
d[h2].ans+=sum(d[h2].m);
}while(h2<r);
for(int i=l;i<h1;i++)inse(d[i].m,-(d[i].num+1));
return;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&d[i].s,&d[i].c,&d[i].m);
}
sort(d+1,d+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(d[i].c==d[N].c&&d[i].s==d[N].s&&d[i].m==d[N].m)
{
d[N].num++;
}else
{
N++;d[N]=d[i];
}
}//去重,并计数
cdq(1,N);
for(int i=1;i<=N;i++)
ans[d[i].num+d[i].ans]+=d[i].num+1;
for(int i=0;i<n;i++)printf("%d\n",ans[i]);
return 0;
}