ACM-ICPC 2018 青島賽區網路預賽 G.Couleur (逆序對、主席樹)
阿新 • • 發佈:2018-12-12
題意:
給出一個數列,每次都會刪去一個數並將原數列分為兩段,問每次刪數前所有段中逆序對的最大數量,強制線上。
思路:
利用主席樹可以每次O(log(n))算出一個點在某個區間的逆序對數量,一開始還沒刪的時候先統計總共有多少逆序對。
對於每次刪除操作,都暴力算出拆分後小區間的逆序對,大區間逆序對再根據小區間來算。
以左邊為小區間為例: del 將要被刪除的點,tot 原先該區間的逆序對數。
①利用主席樹暴力算左區間內部有多少逆序對 lcnt 。
②計算左區間中每個值和 ( del + 右區間 ) 的逆序對數量之和,也就是對於左區間中的點來說原先跨區間的逆序對數量和 temp 。
③計算 del 與右區間的逆序對數量 dcnt 。
④剩下左區間的逆序對數量即為 lcnt ,右區間逆序對數量為 tot - lcnt - temp - dcnt 。
如果右邊為小區間也類似計算就可以了。
答案值可以用multiset來維護,已經刪除的位置可以用set儲存,每個區間的逆序對數可以用map來儲存。
AC程式碼:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cstdlib> #include<utility> #include<algorithm> #include<utility> #include<queue> #include<vector> #include<set> #include<stack> #include<cmath> #include<map> #include<ctime> #include<functional> #include<bitset> #define P pair<int,int> #define ll long long #define ull unsigned long long #define lson id*2,l,mid #define rson id*2+1,mid+1,r #define ls id*2 #define rs (id*2+1) #define Mod(a,b) a<b?a:a%b+b #define cl0(a) memset(a,0,sizeof(a)) #define cl1(a) memset(a,-1,sizeof(a)) using namespace std; const ll M = 1e9 + 7; const ll INF = 1e9; const int N = 410; const double _e = 10e-6; const int maxn = 1e5 + 10; const int matSize = 9; const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 }; const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 }; int x, y; int n, m; int c[maxn]; map<int, ll> mp; set<int> s; multiset<ll> ms; struct chairtree { int l, r; ll cnt; }t[maxn << 5]; int root[maxn], a[maxn]; int lens, tot; int build(int l, int r) { int id = ++tot; t[id].cnt = 0; if (l == r) return id; int mid = (l + r) / 2; t[id].l = build(l, mid); t[id].r = build(mid + 1, r); return id; } int update(int id, int l, int r, int x) { int now = ++tot; t[now].cnt = t[id].cnt + 1; if (l == r) return now; int mid = (l + r) / 2; if (x <= mid) { t[now].r = t[id].r; t[now].l = update(t[id].l, l, mid, x); } else { t[now].l = t[id].l; t[now].r = update(t[id].r, mid + 1, r, x); } return now; } ll query(int lid, int rid, int l, int r, int _l, int _r) { if (l == _l&&r == _r) return t[rid].cnt - t[lid].cnt; int mid = (l + r) / 2; if (_r <= mid) return query(t[lid].l, t[rid].l, l, mid, _l, _r); else if (_l > mid) return query(t[lid].r, t[rid].r, mid + 1, r, _l, _r); else return query(t[lid].l, t[rid].l, l, mid, _l, mid) + query(t[lid].r, t[rid].r, mid + 1, r, mid + 1, _r); } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); ll ans = 0; tot = 0; lens = n + 1; mp.clear(); s.clear(); ms.clear(); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) scanf("%d", &c[i]); root[0] = build(1, lens); for (int i = 1; i <= n; i++) { ans += query(root[0], root[i - 1], 1, lens, a[i] + 1, lens); root[i] = update(root[i - 1], 1, lens, a[i]); } s.insert(0); s.insert(lens); ms.insert(ans); mp[0] = ans; for (int i = 1; i < n; i++) { printf("%lld ", ans); if (ans == 0)continue; int del = c[i] ^ ans; auto it = s.upper_bound(del); int l, r = *it; it--; l = *it; ll lcnt, rcnt, temp = 0; if (del - l <= r - del) { lcnt = 0, rcnt = mp[l]; ms.erase(ms.lower_bound(rcnt)); for (int j = l + 1; j < del; j++) { if (a[j] > 1) { temp += query(root[j], root[del - 1], 1, lens, 1, a[j] - 1); rcnt -= query(root[del - 1], root[r - 1], 1, lens, 1, a[j] - 1); } } lcnt += temp; rcnt -= temp; if (a[del] > 1) rcnt -= query(root[del], root[r - 1], 1, lens, 1, a[del] - 1); } else { lcnt = mp[l], rcnt = 0; ms.erase(ms.lower_bound(lcnt)); for (int j = del + 1; j < r; j++) { temp += query(root[del], root[j - 1], 1, lens, a[j] + 1, lens); lcnt -= query(root[l], root[del], 1, lens, a[j] + 1, lens); } lcnt -= temp; rcnt += temp; lcnt -= query(root[l], root[del - 1], 1, lens, a[del] + 1, lens); } ms.insert(lcnt); ms.insert(rcnt); mp[l] = lcnt; mp[del] = rcnt; auto itt = ms.end(); itt--; ans = *itt; s.insert(del); } printf("%lld\n", ans); } return 0; }