BZOJ3262: 陌上花开【三维偏序】

题目描述:

有n朵花,每朵花有三个属性:花形(s)、颜色©、气味(m),用三个整数表示。
现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量。
定义一朵花A比另一朵花B要美丽,当且仅Sa>=Sb,Ca>=Cb,Ma>=Mb。
显然,两朵花可能有同样的属性。需要统计出评出每个等级的花的数量。

第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值。
以下N行,每行三个整数si, ci, mi (1 <= si, ci, mi <= K),表示第i朵花的属性

题目分析:

三维偏序模板。
但是要注意的是花的三个属性都相同的时候,在cdq分治中计算贡献时的顺序问题,如果当cdq的那一维相同时要先将所有的值加到树状数组中再来统计,如果不想加这个特判,就在外面把相同的合并,加贡献的时候注意加的是个数。

第一维排序,第二维cdq,第三维树状数组(这是比较方便的写法,当然还有很多种写法)。

这里的cdq可以只带两个参数表示操作区间,然后对第二维进行归并排序。
也可以带四个参数表示第二维的权值区间和操作区间,相当于用权值的mid来划分操作,就不用归并了。(虽然代码反而变长了。。)

Code:

#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 200005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,Q,N,ans[maxn],num[maxn];
int arr[maxn];
void upd(int i,int d){for(;i<=N;i+=i&-i) arr[i]+=d;}
int qsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
struct node{
	int x,y,z,ans,c; node(){}
	node(int x,int y,int z,int ans,int c):x(x),y(y),z(z),ans(ans),c(c){}
	bool operator < (const node &p)const{return x==p.x?y==p.y?z<p.z:y<p.y:x<p.x;}
}a[maxn],b[maxn];
void solve(int l,int r,int ql,int qr){
	if(ql>=qr) return;
	if(l==r){
		for(int i=ql;i<=qr;i++) a[i].ans+=qsum(a[i].z),upd(a[i].z,a[i].c);
		for(int i=ql;i<=qr;i++) upd(a[i].z,-a[i].c);
		return;
	}
	int mid=(l+r)>>1,lp=ql,rp;
	for(int i=ql;i<=qr;i++) 
		if(a[i].y<=mid) b[lp++]=a[i],upd(a[i].z,a[i].c);
		else a[i].ans+=qsum(a[i].z);
	rp=lp;
	for(int i=ql;i<=qr;i++)
		if(a[i].y<=mid) upd(a[i].z,-a[i].c);
		else b[rp++]=a[i];
	for(int i=ql;i<=qr;i++) a[i]=b[i];
	solve(l,mid,ql,lp-1),solve(mid+1,r,lp,qr);
}
int main()
{
	read(n),read(N);
	for(int i=1;i<=n;i++) read(b[i].x),read(b[i].y),read(b[i].z),b[i].c=1;
	sort(b+1,b+1+n);
	for(int i=1;i<=n;i++) 
		if(b[i].x!=b[i-1].x||b[i].y!=b[i-1].y||b[i].z!=b[i-1].z) a[++m]=b[i];
		else a[m].c++;
	solve(1,N,1,m);
	for(int i=1;i<=m;i++) num[a[i].ans+a[i].c-1]+=a[i].c;
	for(int i=0;i<n;i++) printf("%d\n",num[i]);
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值