二維偏序


二維/三維偏序

定義:

形如 \(x_i<x_j\)\(y_i<y_j\) 之類的約束條件,我們可以稱為二維偏序。
逆序對就是一個非常經典的二位偏序。

解決:

如果按照暴力想法,我們 \(O(n^2)\) 的時間枚舉 \(i,j\) ,這樣太慢了。

處理第 \(i\) 位時,我們已經處理過 \([0,i-1]\) 的數量,那么我們可不可以用一個數據結構記錄一下之前的情況呢?

這就引出了二維偏序。

我們把第一維從小到大排序,然后遍歷,將第二位插入樹狀數組中,每次查詢,即可解決問題。

其中,因為只擁有這些約束條件,我們需要離散化,減小空間

例子:

就以逆序對作為例子:

先將這個序列離散化,反向排序,此時我們轉化成: 記錄當前點權值大於之前點的個數之和,也就是正序對。

我們定義一個樹狀數組為: 記錄 \(i\) 前小於 \(val[i]\) 的點權的個數,於是我們就可以在遍歷時 插入,查詢。

代碼:

void add(){...}
int query(){...}
...
for(int i=1;i<=n;i++){
    add(val[i],1);
    ans+=query(val[i]-1);
}

例題:

CF1311F Moving Points

我們分三種情況討論:

  1. \(x_i<x_j\),\(v_i<=v_j\) : 最小距離就是當前兩點距離。
  2. \(x_i<x_j\),\(v_i>v_j\) : 肯定能追上,最小距離為 \(0\)
  3. \(x_i<x_j\),\(v_i\geq0,v_j\leq 0\) : 相遇問題,最小距離為 \(0\)

那么這個問題就轉換成了 求\(\sum_{i=1}^{n}\sum_{j=i+1}^{n} x_i<x_j\And v_i<v_j\) 的點對的個數。

我們於是定義兩個樹狀數組,第一個 \(t[1]\) 是已經維護了的點的數量 \(A\) ,第二個 \(t[2]\) 是已經維護的點到原點的距離之和 \(B\)

所以公式為 \(\sum_{i=1}^n A*x[i]-B\)
代碼:

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define lowbit(x) x&-x
const int N=2e5+5;
int t[2][N],b[N],n,m,ans;
struct node{
    int x,v;
}e[N];
bool cmp(node a,node b){
    return a.x==b.x?a.v<b.v:a.x<b.x;
}
void add(int x,int z,int t[]){
    for(;x<N;x+=lowbit(x)) t[x]+=z;
}
int query(int x,int t[]){
    int res=0;
    for(;x;x-=lowbit(x)) res+=t[x];
    return res;
}
signed main()
{
    cin>>n;
    for(int i=1;i<=n;i++) scanf("%lld",&e[i].x);
    for(int i=1;i<=n;i++) scanf("%lld",&e[i].v),b[i]=e[i].v;
    sort(e+1,e+n+1,cmp); sort(b+1,b+1+n);
    int m=unique(b+1,b+1+n)-b;
    for(int i=1;i<=n;i++) e[i].v=lower_bound(b+1,b+1+m,e[i].v)-b;
    for(int i=1;i<=n;i++){
        add(e[i].v,e[i].x,t[0]); add(e[i].v,1,t[1]);
        ans+=query(e[i].v,t[1])*e[i].x-query(e[i].v,t[0]);
        //已經維護了速度小於v[i]點的數量      已經維護的速度小於v[i]點到原點距離之和
    }
    cout<<ans<<endl;
    return 0;
}


免責聲明!

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



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