P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)
生活随笔
收集整理的這篇文章主要介紹了
P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
P3157 [CQOI2011]動態逆序對
題目描述
對于序列A,它的逆序對數定義為滿足i<j,且Ai>Aj的數對(i,j)的個數。給1到n的一個排列,按照某種順序依次刪除m個元素,你的任務是在每次刪除一個元素之前統計整個序列的逆序對數。
輸入格式
輸入第一行包含兩個整數n和m,即初始元素的個數和刪除的元素個數。以下n行每行包含一個1到n之間的正整數,即初始排列。以下m行每行一個正整數,依次為每次刪除的元素。
輸出格式
輸出包含m行,依次為刪除每個元素之前,逆序對的個數。
輸入輸出樣例
輸入 #1復制
5 4 1 5 3 4 2 5 1 4 2輸出 #1復制
5 2 2 1樣例解釋 (1,5,3,4,2)?(1,3,4,2)?(3,4,2)?(3,2)?(3)。說明/提示
N<=100000 M<=50000
思路:
如果不是很懂CDQ的話,建議先看下這個。
CDQ 分治解決和點對有關的問題
然后就是一個很裸的CDQ分治解決三維偏序的問題,
我們知道逆序對可以是放在一個平面2維坐標系中,點在數組中的位置pos為x,點的數值為y
兩個點$ (x1,y1)$ , $ (x2,y2)$
如果滿足:
? $ x1<x2$ 同時 $ y1>y2$
那么這個點對是一個逆序對。
那么CDQ問題的3個維度分別可以是:
時間,x坐標,y坐標。
第一維護直接sort解決,x坐標在歸并排序中解決升序,y坐標用樹狀數組這個數據結構解決。
時間復雜度: $ O(n*log^2(n) )$
代碼有一些備注。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #include <vector> #include <iomanip> #define ALL(x) (x).begin(), (x).end() #define sz(a) int(a.size()) #define rep(i,x,n) for(int i=x;i<n;i++) #define repd(i,x,n) for(int i=x;i<=n;i++) #define pii pair<int,int> #define pll pair<long long ,long long> #define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) #define MS0(X) memset((X), 0, sizeof((X))) #define MSC0(X) memset((X), '\0', sizeof((X))) #define pb push_back #define mp make_pair #define fi first #define se second #define eps 1e-6 #define gg(x) getInt(&x) #define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl #define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c)) #define du2(a,b) scanf("%d %d",&(a),&(b)) #define du1(a) scanf("%d",&(a)); using namespace std; typedef long long ll; ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} ll lcm(ll a, ll b) {return a / gcd(a, b) * b;} ll powmod(ll a, ll b, ll MOD) {a %= MOD; if (a == 0ll) {return 0ll;} ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;} void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}} void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}inline void getInt(int *p); const int maxn = 1000010; const int inf = 0x3f3f3f3f; /*** TEMPLATE CODE * * STARTS HERE ***/ struct node {int t;int x;int y;node() {}node(int tt, int xx, int yy){t = tt;x = xx;y = yy;}bool operator < (const node &bb )const{if (x != bb.x) {return x < bb.x;} else {return y < bb.y;}} } a[maxn], b[maxn]; bool cmpx(node &aa, node &bb) {return aa.t < bb.t; } int n, m; int pos[maxn]; ll ans[maxn];ll tree[maxn]; int lowbit(int x) {return -x & x; } ll ask(int x) {ll res = 0ll;while (x) {res += tree[x];x -= lowbit(x);}return res; } void add(int x, ll val) {while (x < maxn) {tree[x] += val;x += lowbit(x);} } void clear(int x) {while (x < maxn) {tree[x] = 0ll;x += lowbit(x);} } void cdq(int l, int r) {if (l == r) {return ;}int mid = (l + r) >> 1;cdq(l, mid);cdq(mid + 1, r);int ql = l;int qr = mid + 1;// 計算在其左邊,數值比其大的貢獻//// 下面是類似歸并排序方式計算貢獻repd(i, l, r) {if (qr > r || (ql <= mid && a[ql] < a[qr])) {add(a[ql].y, 1);b[i] = a[ql++];} else {ans[a[qr].t] += ask(n) - ask(a[qr].y );b[i] = a[qr++];}}repd(i, l, mid) {clear(a[i].y);}repd(i, l, r) {a[i] = b[i];}// 計算在其右邊,數值比其小的貢獻// 因為上面已經歸并排序過了,所以這個區間中是以x為升序的。for (int i = r; i >= l; --i) {if (a[i].t <= mid) {add(a[i].y, 1);} else {ans[a[i].t] += ask(a[i].y);}}repd(i, l, r) {clear(a[i].y);} } int main() {//freopen("D:\\code\\text\\input.txt","r",stdin);//freopen("D:\\code\\text\\output.txt","w",stdout);du2(n, m);repd(i, 1, n) {a[i].x = i;du1(a[i].y);pos[a[i].y] = i;}int tm = n;repd(i, 1, m) {int x;du1(x);a[pos[x]].t = tm--;}repd(i, 1, n) {if (!a[i].t) {a[i].t = tm--;}}sort(a + 1, a + 1 + n, cmpx);// 按照time排序,確保時間維度time是升序的。cdq(1, n);repd(i, 1, n) {ans[i] += ans[i - 1];}for (int i = n; i >= n - m + 1; --i) {printf("%lld\n", ans[i]);}return 0; }inline void getInt(int *p) {char ch;do {ch = getchar();} while (ch == ' ' || ch == '\n');if (ch == '-') {*p = -(getchar() - '0');while ((ch = getchar()) >= '0' && ch <= '9') {*p = *p * 10 - ch + '0';}} else {*p = ch - '0';while ((ch = getchar()) >= '0' && ch <= '9') {*p = *p * 10 + ch - '0';}} }轉載于:https://www.cnblogs.com/qieqiemin/p/11605173.html
總結
以上是生活随笔為你收集整理的P3157 [CQOI2011]动态逆序对 (CDQ解决三维偏序问题)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CDQ 分治解决和点对有关的问题
- 下一篇: P4390 [BOI2007]Mokia