淺談CDQ分治


Ⅰ、預備知識

整體二分???

Ⅱ、拋出問題

我們先來看一道洛谷的模板題

題目背景

這是一道模板題
可以使用bitset(不會),CDQ分治,K-DTree(不會)等方式解決。

題目描述

\(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\)的數量

輸入輸出格式

輸入格式:

第一行兩個整數\(n、k\),分別表示元素數量和最大屬性值。
之后\(n\)行,每行三個整數\(a_i\)\(b_i\)\(c_i\),分別表示三個屬性值。

輸出格式:

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

輸入輸出樣例

輸入樣例#1:

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

輸出樣例#1:

3
1
3
0
1
0
1
0
0
1

說明:

\(1\leq N\leq100000,1\leq k\leq200000\)

Ⅲ、分析問題

CDQ分治,有國家隊某巨佬發明(仿佛是插頭dp的論文作者???),主要用於解決帶修改,查詢,可排序序列的一系列問題,僅可支持離線操作
CDQ分治的主要步驟有以下幾點:
1、讀入(廢話)
1、將已經讀入好的數據按照某關鍵字排序
2、設當前區間為\([l,r]\),遞歸處理左區間\([l,mid]\)和右區間\([mid+1,r]\),計算左區間的修改操作對右區間的影響(一般用樹狀數組等數據結構維護)
3、清除數據結構內的修改數據
本題又叫三維偏序問題,是CDQ分治的經典題型
先按照第一維(即\(a_i\))排序,這樣就將問題轉化到了二維
設當前區間為\([l,r]\)
\([l,mid]\)\([mid+1,r]\)分別按照第二維排序,此時在左區間中的\(a\)均小於有區間中的\(a\)(保證第一維),設左區間已訪問到\(pl\),右區間已訪問到\(pr\)\((l\leq pl\leq mid,mid+1\leq pr\leq r)\)
\(b[pl]<=b[pr]\)時(保證第二維),即將\(pl\)點的\(c\)值加入樹狀數組
統計比\(pr\)點的\(c\)值小或等於的點的數量(保證第三維)
詳見代碼

#include<bits/stdc++.h>
#define ll long long
#define INF 2147483647
#define mem(i,j) memset(i,j,sizeof(i))
#define F(i,j,n) for(register int i=j;i<=n;i++)
#define lowbit(i) i&(-i)//樹狀數組
using namespace std;
struct hahaha{
	int x,y,z,ans,cnt;//x,y,z分別對應a,b,c;ans表示題目中的f(i),即三維都小於等於i的數量,cnt表示x,y,z相等的點的數量,若只出現一次,則cnt=1
}f[100010],s[100010];//f為輸入數據,s為處理后數據
int nn,n,m,ans[100010],c[200010];//c是樹狀數組上的點,ans為最終答案
inline int read(){
	int datta=0;char chchc=getchar();bool okoko=0;
	while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
	while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
	return okoko?-datta:datta;
}
inline bool cmpx(hahaha a,hahaha b){//以x為第一關鍵字排序
	return a.x==b.x?a.y==b.y?a.z<b.z:a.y<b.y:a.x<b.x;
}
inline bool cmpy(hahaha a,hahaha b){//以y為第一關鍵字排序
	return a.y==b.y?a.z<b.z:a.y<b.y;
}
inline void add(int x,int v){//樹狀數組修改
	for(int i=x;i<=m;i+=lowbit(i))
		c[i]+=v;
}
inline int ask(int x){//樹狀數組查詢
	int res=0;
	for(int i=x;i;i-=lowbit(i))
		res+=c[i];
	return res;
}
class CDQ_DC{//之所以用class寫是為了裝逼
	private:
	public:
	inline void CDQ(int l,int r){
		if(l==r)//邊界條件
			return ;
		int mid=(l+r)>>1;
		CDQ(l,mid);
		CDQ(mid+1,r);//遞歸處理左右區間
		sort(s+l,s+mid+1,cmpy);
		sort(s+mid+1,s+r+1,cmpy);//按y排序
		int pl=l,pr=mid+1;
		while(pr<=r){
			while(pl<=mid&&s[pl].y<=s[pr].y)
				add(s[pl].z,s[pl].cnt),pl++;//加點
			s[pr].ans+=ask(s[pr].z);//處理pr的ans
			pr++;
		}
		F(i,l,pl-1)
			add(s[i].z,-s[i].cnt);//清空樹狀數組
	}
}C;
int main(){
	nn=read();m=read();
	F(i,1,nn)
		f[i].x=read(),f[i].y=read(),f[i].z=read();
	sort(f+1,f+nn+1,cmpx);//按x排序
	int ct=0;
	F(i,1,nn){
		ct++;
		if(f[i].x!=f[i+1].x||f[i].y!=f[i+1].y||f[i].z!=f[i+1].z){
			s[++n]=f[i];
			s[n].cnt=ct;//處理數據
			ct=0;
		}
	}
	C.CDQ(1,n);
	F(i,1,n)
		ans[s[i].ans+s[i].cnt-1]+=s[i].cnt;//處理最后答案
	F(i,0,nn-1)
		printf("%d\n",ans[i]);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM