嘟嘟嘟
雙倍經驗
這道題只要想明白了就和\(cdq\)分治的板兒沒什麼區別了,然而如果想不明白就會像我一樣磨嘰了一晚上。
刪數不好辦,於是離線倒序改成加數。
考慮加上乙個數\(a_i\)形成的逆序對:1.在他前面且比他大的。2.在他後面且比他小的。
因為數字是動態新增的,所以上述的數必須是在他之前新增的!
如果給每乙個數三個屬性:\(x, tim, val\),分別表示這個數的位置,新增時間和大小。那麼上面的兩條就可以形式化的寫成:
$ x_j < x_i, tim_j < tim_i,val_j > val_i\(
\)x_j > x_i, tim_j < tim_i, val_j < val_i\(
這不就是陌上花開嗎!
於是我們按\)x\(排序,然後歸併排序\)tim\(,並用樹狀陣列維護\)val$。
需要注意的是成立條件有兩條,因此我們在分治的每一層要歸併兩次,第一次統計右邊的數形成的逆序對個數,第二次統計左邊的數形成的逆序對個數。
#include#include#include#include#include#include#include#include#include#includeusing namespace std;
#define enter puts("")
#define space putchar(' ')
#define mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int inf = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 1e5 + 5;
inline ll read()
inline void write(ll x)
int n, m, pos[maxn];
struct node
a[maxn], t[maxn];
int c[maxn];
int lowbit(int x)
void erase(int pos)
void add(int pos, int d)
int query(int pos)
ll ans[maxn];
void cdqsolve(int l, int r)
else
}for(int i = l; i <= mid; ++i) erase(a[i].val);
id1 = l; id2 = mid + 1;
for(int i = l; i <= r; ++i)
else
}for(int i = mid + 1; i <= r; ++i) erase(a[i].val);
for(int i = l; i <= r; ++i) a[i] = t[i];
}int main()
int t = m;
for(int i = 1; i <= m; ++i)
cdqsolve(1, n);
for(int i = 1; i <= m; ++i) ans[i] = ans[i - 1] + ans[i];
for(int i = m; i; --i) write(ans[i]), enter;
return 0;
}
Cqoi2011 動態逆序對
主席樹套樹狀陣列。主席樹第一題。鏈結靜態的逆序對問題很簡單,用線段樹或者是樹狀陣列即可解決。現在的問題是如何解決一道動態的逆序對問題?我們先把所有的逆序對統計出來。每次刪除數,我們可以把這個數對於逆序對個數的貢獻刪除出去。這個貢獻如何統計呢?front i 記錄i位置之前有多少個數比這個數大 bac...
CQOI2011 動態逆序對
這是一道cdq分治的好題,這道題的前置知識是cdq分治解決三維偏序問題,如果不會這個話請先自行學習。首先第乙個答案很顯然就是逆序對的數量,然後後面每次的刪除操作,我們考慮把這個被刪除的點原先的貢獻從答案中拿掉。我們用y表示這個點的數,del表示第幾個被刪除,若沒有被刪除則del m 1 考慮每個點的...
CQOI2011 動態逆序對
點此看題 考慮c dq cdqcd q,有三個值 t,d,v t,d,v t,d,v 要求t i t iti d i d idi v i vj v i v j vi vj 很容易看出來是三維偏序的板題,我們先保證t tt的有序,cdq cdqcd q的時候排序d dd,然後用樹狀陣列查詢v vv,貼...