偏序關係:
大概就是,滿足自反性,反對稱性,傳遞性。
將嚴格偏序關係建圖,可以得到一個DAG(有向無環圖)
二維偏序問題是:給定 \(n\) 個元素,每個元素有\(2\)個屬性,定義某種偏序關係,對於所有 \(x_i\) ,求 \(x_j \prec x_i\) 的數量。
一種基本的操作方法是,某個屬性按大小排序,另一個屬性利用樹狀陣列/線段樹進行數量,求和等操作。這種處理方式要依靠在詢問可以離線的前提下。
這裡有個經典例題,二維數點問題。
P2163 [SHOI2007] 園丁的煩惱
大概題意:
第一行有兩個整數 \(n, m\),分別表示點個數和詢問次數。
接下來 \(n\) 行,每行兩個整數 \(x, y\),表示存在座標為 \((x, y)\) 的點。有可能存在點位於同一座標。
接下來 \(m\) 行,每行四個整數 \(a, b, c, d\),表示查詢以 \((a, b)\) 為左下角,\((c, d)\) 為右上角的矩形內部(包括邊界)有多少個點。
\(0 \le n \le 5 \times 10^5\),\(1 \le m \le 5 \times 10^5\),\(0 \le x, y, a, b, c, d \le 10^7\),\(a \le c\),\(b \le d\)。
初步的想法:對於 \(n\) ,\(m\) 較小的情況,可以用二維字首和。
可以巧妙的用每個點 \(x\) 值的處理順序,分隔開詢問矩形的 \(x\) 區間的影響。對於 \(y\) 值,用樹狀陣列查詢需要的範圍。
可以想到以下做法:
對所有的詢問離線。將每個矩形拆分成 \((a-1,b-1)(a-1,d)(c,b-1)(c,d)\) 四個點,像字首和一樣算貢獻。
依次處理每一個點,將詢問點中 \(x’\) 之前的點的 \(y\) 值全部加入樹狀陣列中,在查詢 \(y'\) 計算貢獻。
Code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
inline int read(){
int f=1,x=0;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return f*x;
}
int n,m;
int cnt1;
struct node{
int x,y;
int id,ff;
bool operator<(const node& p)const{
if(x==p.x)return y<p.y;
else return x<p.x;
}
}a[N],b[N*4];
int ans[N];
int mp[N*3],len;
int c[N*3];
inline int lowbit(int x){
return x&-x;
}
inline void upd(int x,int w){
for(int i=x;i<=len;i+=lowbit(i)){
c[i]+=w;
}
}
inline int qur(int x){
int res=0;
for(int i=x;i;i-=lowbit(i)){
res+=c[i];
}
return res;
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
a[i].x=read(),a[i].y=read();
mp[++len]=a[i].y;
}
sort(a+1,a+1+n);
for(int i=1;i<=m;i++){
int xa=read(),ya=read(),xb=read(),yb=read();
b[++cnt1]={xa-1,ya-1,i,1};
b[++cnt1]={xa-1,yb,i,-1};
b[++cnt1]={xb,ya-1,i,-1};
b[++cnt1]={xb,yb,i,1};
mp[++len]=ya-1,mp[++len]=yb;
}
sort(b+1,b+1+cnt1);
sort(mp+1,mp+1+len);
len=unique(mp+1,mp+1+len)-mp-1;
for(int i=1;i<=n;i++){
a[i].y=lower_bound(mp+1,mp+1+len,a[i].y)-mp;
}
for(int i=1;i<=cnt1;i++){
b[i].y=lower_bound(mp+1,mp+1+len,b[i].y)-mp;
}
int now=0;
for(int i=1;i<=cnt1;i++){
int x=b[i].x,y=b[i].y;
int id=b[i].id,ff=b[i].ff;
while(now+1<=n&&a[now+1].x<=x){
now++;
upd(a[now].y,1);
}
ans[id]+=qur(y)*ff;
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}
當然,更多的並不是這樣明顯的二維偏序關係,常常需要觀察得出偏序關係是什麼,應該怎麼處理。
例題1:P8844 [傳智杯 #4 初賽] 小卡與落葉
我寫的:做法