P3810 【模板】三维偏序(陌上花开)

\(\color{#0066ff}{题目描述}\)

有 n 个元素,第 i 个元素有 \(a_i 、b_i 、c_i\) 三个属性,设 f(i) 表示满足 \(a_j \leq a_i 且 b_j \leq b_i 且 c_j \leq c_i\) 的 j 的数量。

对于 \(d \in [0, n)\) ,求 \(f(i) = d\) 的数量

\(\color{#0066ff}{输入格式}\)

第一行两个整数 n、 k,分别表示元素数量和最大属性值。

之后 n 行,每行三个整数 \(a_i 、b_i 、c_i\) ,分别表示三个属性值。

\(\color{#0066ff}{输出格式}\)

输出 n 行,第 \(d + 1\) 行表示 \(f(i) = d\) 的 i 的数量。

\(\color{#0066ff}{输入样例}\)

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

\(\color{#0066ff}{输出样例}\)

3
1
3
0
1
0
1
0
0
1

\(\color{#0066ff}{数据范围与提示}\)

\(1\leq n \leq 100000,1\leq k\leq 200000\)

\(\color{#0066ff}{题解}\)

cdq 分治

对序列分治,每次统计左区间每个元素对于右区间每个元素的贡献

首先分别以x,y,z为1,2,3关键字排序

那么现在x是有序的

然后对序列cdq分治,每次把左右区间按y,z排序

这样保证了左区间所有x\(\leq\)右区间所有x,且左右区间y有序

维护左区间的指针,for右区间每一个元素,只要左区间指针的元素y比当前小,就在权值树状数组上以z为下标++

然后用当前的z收集ans

注意千万不要单开ans数组收集答案

因为当前元素在每次sort后会变成其他的,这样收集就乱了

最后开桶统计即可

#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define _ 0
#define LL long long
#define Space putchar(' ')
#define Enter putchar('\n')
#define fuu(x,y,z) for(int x=(y),x##end=z;x<=x##end;x++)
#define fu(x,y,z)  for(int x=(y),x##end=z;x<x##end;x++)
#define fdd(x,y,z) for(int x=(y),x##end=z;x>=x##end;x--)
#define fd(x,y,z)  for(int x=(y),x##end=z;x>x##end;x--)
#define mem(x,y)   memset(x,y,sizeof(x))
#ifndef olinr
inline char getc()
{
	static char buf[100001],*p1=buf,*p2=buf;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100001,stdin),p1==p2)? EOF:*p1++;
}
#else
#define getc() getchar()
#endif
template<typename T>inline void in(T &x)
{
	int f=1; char ch; x=0;
	while(!isdigit(ch=getc()))(ch=='-')&&(f=-f);
	while(isdigit(ch)) x=x*10+(ch^48),ch=getc();
	x*=f;
}
int n,k;
struct BIT
{
	private:
		int c[205050];
		#define low(x) ((x)&(-x))
	public:
		inline void add(int pos,int v) {for(int i=pos;i<=k;i+=low(i)) c[i]+=v;}
		inline int query(int pos) {int ans=0; for(int i=pos;i;i-=low(i)) ans+=c[i]; return ans;} 
};
struct node
{
	int x,y,z;
	int num,ans;
	friend bool operator != (const node &a,const node &b)
	{
		return a.x!=b.x||a.y!=b.y||a.z!=b.z;
	}
}e[300000];
BIT t;
int ans[300000];
inline void cdq(int l,int r)
{
	if(l==r) return ;
	int mid=(l+r)>>1;
	cdq(l,mid);cdq(mid+1,r);
	std::sort(e+l,e+mid+1,[](const node &a,const node &b){return (a.y<b.y)||(a.y==b.y&&a.z<b.z);});
	std::sort(e+mid+1,e+r+1,[](const node &a,const node &b){return (a.y<b.y)||(a.y==b.y&&a.z<b.z);});
	int now=l;
	fuu(i,mid+1,r)
	{
		//每一个都会产生贡献,所以是num
		while(now<=mid&&e[now].y<=e[i].y) t.add(e[now].z,e[now].num),now++;
		e[i].ans+=t.query(e[i].z);
	}
	fuu(i,l,now-1) t.add(e[i].z,-e[i].num);
}
int main()
{
	in(n),in(k);
	fuu(i,1,n) in(e[i].x),in(e[i].y),in(e[i].z),e[i].num=1;
	std::sort(e+1,e+n+1,[](const node &a,const node &b){return (a.x<b.x)||(a.x==b.x&&(a.y<b.y))||(a.x==b.x&&a.y==b.y&&a.z<b.z);});
	int tmp=0;
	//去重,相同的压一起(跟自己相同的算在ans的一部分,但自己对自己无贡献)
	fuu(i,1,n)
	{  
		if(e[i]!=e[i-1]) e[++tmp]=e[i];
		else e[tmp].num++;
	} 
	cdq(1,tmp);
	//ans里面套的是当前元素的答案(要加上相同的-自己)
	//外面因为跟自己相同的都是成立的,所以+=个数
	fuu(i,1,tmp) ans[e[i].ans+e[i].num-1]+=e[i].num;
	fuu(i,0,n-1) printf("%d\n",ans[i]);
	return ~~(0^_^0);
}
posted @ 2018-12-06 21:11  olinr  阅读(131)  评论(0编辑  收藏  举报