日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

P3157 动态逆序对 ,树状数组套动态开点线段树

發布時間:2023/12/3 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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+1i+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 动态逆序对 ,树状数组套动态开点线段树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。