CDQ分治

CDQ分治

论文 oi.wiki


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

题目描述

有 $ 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 \ne i $ 的 \(j\) 的数量。

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

提示

$ 1 \leq n \leq 10^5$,$1 \leq a_i, b_i, c_i \le k \leq 2 \times 10^5 $。

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define fd(i,a,b) for(int i=a,_i=b;i<=_i;i=-~i)
#define bd(i,a,b) for(int i=a,_i=b;i>=_i;i=~-i)
#define Db(a,b,ans) cout<<a<<';'<<b<<','<<ans<<endl
using namespace std;

const int N=1e5+509,M=2e5+509,mod=998244353;
int m,t,res[N],n,k;

struct node
{
	int a,b,c,cnt,res;
	bool operator != (node x)
	{
		if(a!=x.a) return 1;
		if(c!=x.c) return 1;
		if(b!=x.b) return 1;
		return 0;
	}
}e[N],ue[N];

struct ST
{
	int c[M];
	
	inline void add(int x,int v)
	{
		for(;x<=k;x+=(x&-x)) c[x]+=v;
	}
	
	inline int ask(int x)
	{
		int re=0;
		for(;x;x-=(x&-x)) re+=c[x];
		return re;
	}
	
}BIT;

bool cmpa(node x,node y)
{
	if(x.a!=y.a) return x.a<y.a;
	if(x.b!=y.b) return x.b<y.b;
	return x.c<y.c;
}

bool cmpb(node x,node y)
{
	if(x.b!=y.b) return x.b<y.b;
	return x.c<y.c;
}

void CDQ(int l,int r)
{
	if(l==r) return;
	int mid=l+((r-l)>>1);
	CDQ(l,mid);
	CDQ(mid+1,r);
	sort(ue+l,ue+mid+1,cmpb);
	sort(ue+mid+1,ue+r+1,cmpb);
	//按b大小排序
	int I=l,J=mid+1;
	while(J<=r)
	{
		while(I<=mid&&ue[I].b<=ue[J].b)
		{
			BIT.add(ue[I].c,ue[I].cnt);
			I++;
			//依次枚举每一个j
			//把所有b[i]<b[j]的点i全部插入树状数组里
		}
		ue[J].res+=BIT.ask(ue[J].c);
		//查询树状数组里有多少个点的c值是小于c[j]
		J++;
	}
	fd(i,l,I-1) BIT.add(ue[i].c,-ue[i].cnt);
	/*
	刚才有:
		while(I<=mid&&ue[I].b<=ue[J].b)
			BIT.add(ue[I].c,ue[I].cnt),I++;
	现在清空树状数组
	*/
	return;
}

signed main()
{
#ifdef FJ
	freopen(".in","m",stdin);
	freopen(".out","w",stdout);
#endif
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>k;
	fd(i,1,n) cin>>e[i].a>>e[i].b>>e[i].c;
	sort(e+1,e+n+1,cmpa);
	fd(i,1,n)
	{
		t++;
		if(e[i]!=e[i+1])
		{
			ue[++m].a=e[i].a;
			ue[m].b=e[i].b;
			ue[m].c=e[i].c;
			ue[m].cnt=t;//有多少个一样的点
			t=0;
		}
	}
	CDQ(1,m);
	fd(i,1,m) res[ue[i].res+ue[i].cnt-1]+=ue[i].cnt;
	//ue[i].res+ue[i].cnt-1:当前点的f(i),累加贡献
	fd(i,0,n-1) cout<<res[i]<<endl;
	return 0; 
}
posted @ 2024-07-03 11:07  whrwlx  阅读(6)  评论(0编辑  收藏  举报