您的位置:首页 > 其它

bzoj 3262: 陌上花开(cdq分治)

2016-09-08 19:26 423 查看

3262: 陌上花开

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 1431  Solved: 644

[Submit][Status][Discuss]

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

HINT

1 <= N <= 100,000, 1 <= K <= 200,000

Source

树套树 CDQ分治

[Submit][Status][Discuss]

题解:cdq分治。

这道题我刚开始是想能不能把三个值搞到一块,然后每次判断只需要对比这个值即可,但是想了很久都没想出来。

那要怎么做呢?我们按照s权值从小到大排序,然后二分s权值区间[l,r],找到权值在[l,r]的区间[x,y],在二分一个中间权值mid,利用二分查找找到值为mid的最后一个数的位置,因为a是从小到大有序的所以只可能是[x,posmid]对后面[posmid+1,y]产生贡献且保证a一定满足条件,所以我们把前面的区间中的数标记,然后按照[x,y]中的数按照c权值从小到大排序,然后按照b的顺序如果有标记就向权值线段树的a[i].m的位置加数,如果没有标记就统计权值线段树中[1,a[i].m]的答案然后添加到ans[a[i].num]。注意那些b,c都相等的花,要放到一起考虑,要一起都加入后再计算。

细节很多,尤其是二分查找!!

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define N 200003
using namespace std;
struct data
{
int a,b,c,num;
int pd;
}a
;
int n,m,tr[N*4],pd[N*4],minn,maxn,num
;
int ans
;
void clear(int now)
{
tr[now]=0;
pd[now]=1;
}
int cmp(data a,data b)
{
return a.a<b.a;
}
int cmp1(data a,data b)
{
return a.b<b.b||a.b==b.b&&a.c<b.c;
}
void pushdown(int now)
{
if (pd[now])
{
pd[now]=0;
clear(now<<1); clear(now<<1|1);
}
}
void update(int now)
{
tr[now]=tr[now<<1]+tr[now<<1|1];
}
void pointchange(int now,int l,int r,int x)
{
if (l==r)
{
tr[now]++;
return;
}
pushdown(now);
int mid=(l+r)/2;
if (x<=mid) pointchange(now<<1,l,mid,x);
else pointchange(now<<1|1,mid+1,r,x);
update(now);
}
int qjsum(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr)
return tr[now];
pushdown(now);
int mid=(l+r)/2;
int ans=0;
if (ll<=mid)   ans+=qjsum(now<<1,l,mid,ll,rr);
if (rr>mid) ans+=qjsum(now<<1|1,mid+1,r,ll,rr);
return ans;
}
int find(int x,int l,int r)
{
int ans=r;
int nowr=r,nowl=l;
if (a[l].a>x)  return l;
if (a[r].a<x)  return r+1;
while (l<=r)
{
int mid=(l+r)/2;
if (a[mid].a>=x) ans=min(ans,mid),r=mid-1;
else  l=mid+1;
}
return ans;
}
void divide(int l,int r,int x,int y)
{
bool f=true;
for (int i=x+1;i<=y;i++)
if (a[i].a!=a[x].a)
{
f=false;
break;
}
if (l>r||x>y) return;
if (l==r||x==y||f)
{
sort(a+x,a+y+1,cmp1);
clear(1);
for (int i=x;i<=y;i++)
if (a[i].c!=a[i+1].c||i==y||a[i].b!=a[i+1].b)
{
pointchange(1,1,m,a[i].c);
int j=i;
while (a[j].c==a[i].c&&a[j].b==a[i].b&&j>=x)
{
int t=qjsum(1,1,m,1,a[j].c);
ans[a[j].num]+=t;
j--;
}
}
else pointchange(1,1,m,a[i].c);
return ;
}
int mid=(l+r)/2;
int posf=x;
int pose=y;
int posm=find(mid+1,x,y)-1;
clear(1);
for (int i=posf;i<=posm;i++)  a[i].pd=1;
for (int i=posm+1;i<=pose;i++)  a[i].pd=0;
sort(a+posf,a+pose+1,cmp1);
for (int i=posf;i<=pose;i++)
if (a[i].c!=a[i+1].c||a[i].b!=a[i+1].b||i==pose)
{
if (a[i].pd==1)   pointchange(1,1,m,a[i].c);
int j=i;
while (a[j].c==a[i].c&&a[j].b==a[i].b&&j>=x)
{
if (a[j].pd==0)
{
int t=qjsum(1,1,m,1,a[j].c);
ans[a[j].num]+=t;
}
j--;
}
}
else
if (a[i].pd==1)  pointchange(1,1,m,a[i].c);
sort(a+posf,a+pose+1,cmp);
divide(l,mid,posf,posm);
divide(mid+1,r,posm+1,pose);
}
int main()
{
freopen("a.in","r",stdin);
freopen("my.out","w",stdout);
scanf("%d%d",&n,&m);
minn=N,maxn=0;
for (int i=1;i<=n;i++)
scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c),a[i].num=i,
minn=min(minn,a[i].a),maxn=max(maxn,a[i].a);
sort(a+1,a+n+1,cmp);
divide(1,maxn,1,n);
for (int i=1;i<=n;i++)
num[ans[i]]++;
for (int i=1;i<=n;i++)
printf("%d\n",num[i]);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: