P3157 動態逆序對 ,樹狀數組套動態開點線段樹

題目

洛谷題目鏈接

題解

在求整體的逆序對的數量時,很好辦,直接用樹狀數組處理即可,不過在這時,我們還需要處理出一個數組pa[] ,其中pa[i] 代表在區間[1,i) 中滿足aj>aiaj 有多少個。

然後我們再利用一個新的BIT ,並且倒着遍歷a[] 數組,這樣我們可以處理出sa[] 數組,其中sa[i] 表示在區間(i,n] 中有多少j 使得,ai>aj

我們考慮刪除一個元素對答案的影響,刪除一個元素以後,那麼要從答案中減去這個元素原來能構成的逆序對的數量,也就是pa[i]sa[i] 。但是呀,這裏有一個問題!就是說pa[i] 或者是sa[i] 中很可能包含有已經被刪除掉的元素了。

解決這個問題有兩個方案,第一就是我們每刪除的一個元素就對所有依賴這個元素的sa[]pa[] 數組全部更新,這樣顯然不合理,遇到極端數據時間複雜度會爆炸。
還有一個解決方案就是,我們把要減去的部分容斥一下,即在已經被刪除的元素裏面,再找於、與ai 元素能構成逆序對的數量t

這樣的話,ans=sa[i]pa[i]t

這個思路聽起來很完美,但是這個t 要怎麼找呢?

其實我們需要的是對所有被刪除的元素建立一顆可修改的主席樹

正常的主席樹想要修改的時候,如果修改了第i 個版本的主席樹,同時要把i+1i+2... 等版本的主席樹都修改,因爲是主席樹跟前一個版本的主席樹是有依賴關係的。

我們發現樹狀數組跟我們的需求非常像,因爲樹狀數組具有區間求和的性質,也具有單點修改時間複雜度O(logn) 的優秀性質。

於是!我們可以用樹狀數組套權值線段樹來解決這個問題。

由於空間所限,我們做線段樹採用動態開點的方法。

其中樹狀數組裏的每一個節點裏麪包含有一顆線段樹。

代碼

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
#define pr(x) cout<<#x<<":"<<x<<endl
#define int long long
const int maxn = 200007;

struct segtree{
    int root[maxn*40];
    int lson[maxn*40];
    int rson[maxn*40];
    int val[maxn*40];
    int idx;
    void ins(int &rt,int l,int r,int pos,int v){
        if(!rt) rt = ++idx;
        val[rt] += v;
        if(l == r) return ;
        int mid = (l+r)/2;
        if(pos <= mid) ins(lson[rt],l,mid,pos,v);
        else ins(rson[rt],mid+1,r,pos,v);
    } 
    int query(int rt,int l,int r,int ul,int ur){
        if(!rt || r < ul || ur < l) return 0;
        if(ul <= l && r <= ur) return val[rt];
        int mid = (l+r)/2;
        int a = query(lson[rt],l,mid,ul,ur);
        int b = query(rson[rt],mid+1,r,ul,ur);
        return a+b;
    }
}seg;

struct BIT{
    int a[maxn];
    void add(int pos,int v){
        for(;pos < maxn;pos += pos & (-pos))
            a[pos] += v;
    }
    int query(int pos){
        int ans = 0;
        for(;pos;pos -= pos & (-pos))
            ans += a[pos];
        return ans;
    }
}b1,b2;
int pos[maxn],a[maxn];
int pa[maxn],sa[maxn];
int n,m;

signed main(){
    long long ans = 0;
    cin>>n>>m;
    for(int i = 1;i <= n;++i){
        scanf("%lld",&a[i]);
        pos[a[i]] = i;
        b1.add(a[i],1);
        ans += pa[i] = b1.query(n) - b1.query(a[i]);
    }
    for(int i = n;i > 0;i--){
        sa[i] = b2.query(a[i]-1);
        b2.add(a[i],1);
    }

    for(int i = 1;i <= m;++i){
        printf("%lld\n",ans);
        int v;scanf("%lld",&v);
        int p = pos[v];p--;
        long long tmp = 0;
        for(;p;p -= p & (-p))
            tmp += seg.query(seg.root[p],0,100001,v+1,100001);
        p = pos[v];
        for(;p;p -= p & (-p))
            tmp -= seg.query(seg.root[p],0,100001,0,v-1);
        p = n;
        for(;p;p -= p & (-p))
            tmp += seg.query(seg.root[p],0,100001,0,v-1);
        ans -= sa[pos[v]] + pa[pos[v]] - tmp;
        p = pos[v];
        for(;p <= n;p += p&(-p))
            seg.ins(seg.root[p],0,100001,v,1);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章