P3157 动态逆序对 ,树状数组套动态开点线段树
題目
洛谷題目鏈接
題解
在求整體的逆序對的數量時,很好辦,直接用樹狀數組處理即可,不過在這時,我們還需要處理出一個數組pa[]pa[]pa[],其中pa[i]pa[i]pa[i]代表在區間[1,i)[1,i)[1,i)中滿足aj>aia_j>a_iaj?>ai?的aja_jaj?有多少個。
然后我們再利用一個新的BITBITBIT,并且倒著遍歷a[]a[]a[]數組,這樣我們可以處理出sa[]sa[]sa[]數組,其中sa[i]sa[i]sa[i]表示在區間(i,n](i,n](i,n]中有多少jjj使得,ai>aja_i > a_jai?>aj?。
我們考慮刪除一個元素對答案的影響,刪除一個元素以后,那么要從答案中減去這個元素原來能構成的逆序對的數量,也就是pa[i]+sa[i]pa[i]+sa[i]pa[i]+sa[i]。但是呀,這里有一個問題!就是說pa[i]pa[i]pa[i]或者是sa[i]sa[i]sa[i]中很可能包含有已經被刪除掉的元素了。
解決這個問題有兩個方案,第一就是我們每刪除的一個元素就對所有依賴這個元素的sa[]、pa[]sa[]、pa[]sa[]、pa[]數組全部更新,這樣顯然不合理,遇到極端數據時間復雜度會爆炸。
還有一個解決方案就是,我們把要減去的部分容斥一下,即在已經被刪除的元素里面,再找于、與aia_iai?元素能構成逆序對的數量ttt。
這樣的話,ans?=sa[i]+pa[i]?tans -= sa[i]+pa[i]-tans?=sa[i]+pa[i]?t。
這個思路聽起來很完美,但是這個ttt要怎么找呢?
其實我們需要的是對所有被刪除的元素建立一顆可修改的主席樹!
正常的主席樹想要修改的時候,如果修改了第iii個版本的主席樹,同時要把i+1、i+2...i+1、i+2...i+1、i+2...等版本的主席樹都修改,因為是主席樹跟前一個版本的主席樹是有依賴關系的。
我們發現樹狀數組跟我們的需求非常像,因為樹狀數組具有區間求和的性質,也具有單點修改時間復雜度O(logn)O(logn)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);} }總結
以上是生活随笔為你收集整理的P3157 动态逆序对 ,树状数组套动态开点线段树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spotify 第三季度营收 3200
- 下一篇: 广汽公布埃安 AION S Max 轿跑