「Hanabi, hanabi……」node
一據說祭典上沒有煙火,Karen 一臉沮喪。c++
「有的哦…… 雖然比不上大型煙花就是了。」git
還好 Shinobu 早有準備,Alice、Ayaya、Karen、Shinobu、Yoko 五人又能繼續愉快地玩耍啦!數組
「噢……!不是有放上天的煙花嘛!」Karen 興奮地喊道。spa
「啊等等……」Yoko 驚呼。Karen 手持點燃引信的煙花,「嗯??」code
Yoko 最但願見到的是排列優美的煙火,固然不會放過這個機會…… 不過期間彷佛已經很少了。get
nn 個煙火排成一排,從左到右高度分別爲 $h_1,h_2,\cdots$,這些高度兩兩不一樣。it
每次 Yoko 能夠選擇兩個相鄰的煙火交換,這樣的交換能夠進行任意屢次。class
每次 Yoko 還能夠選擇兩個不相鄰的煙火交換,但這樣的交換至多進行一次。im
你的任務是幫助 Yoko 用最少次數的交換,使這些煙火從左到右的高度遞增。
第一行包含一個正整數 $n$。
第二行包含 $n$ 個正整數 $h_1,h_2,\cdots$,相鄰整數之間用一個空格隔開。
輸出一個整數,表示最少的交換次數。
5 3 5 4 1 2
5
一開始,$5$ 個煙火的高度依次爲 $3,5,4,1,2$。
第 $1$ 次,交換第 $4$ 根菸火和第 $5$ 根菸火,交換後煙火的高度依次爲 $3,5,4,2,1$。
第 $2$ 次,交換第 $3$ 根菸火和第 $4$ 根菸火,交換後煙火的高度依次爲 $3,5,2,4,1$。
第 $3$ 次,交換第 $1$ 根菸火和第 $2$ 根菸火,交換後煙火的高度依次爲 $5,3,2,4,1$。
第 $4$ 次,交換第 $2$ 根菸火和第 $3$ 根菸火,交換後煙火的高度依次爲 $5,2,3,4,1$。
第 $5$ 次,交換第 $1$ 根菸火和第 $5$ 根菸火,交換後煙火的高度依次爲 $1,2,3,4,5$。
能夠證實這是交換次數最少的方案。
#include<bits/stdc++.h> #define LL long long LL in() { char ch; LL x = 0, f = 1; while(!isdigit(ch = getchar()))(ch == '-') && (f = -f); for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48)); return x * f; } const int maxn = 3e5 + 10; struct Tree { protected: int st[maxn]; int low(int x) { return x & (-x); } int siz; public: void resize(int n) { siz = n; } void add(int pos) { while(pos <= siz) st[pos]++, pos += low(pos); } int query(int pos) { int re = 0; while(pos) re += st[pos], pos -= low(pos); return re; } }s; struct node { node *ch[2]; int num; node(int num = 0): num(num) { ch[0] = ch[1] = NULL; } }; int n, a[maxn]; node *root[maxn]; int st1[maxn], st2[maxn], top1, top2, to[maxn], ans[maxn]; LL tot; void init() { root[0] = new node(); root[0]->ch[0] = root[0]->ch[1] = root[0]; } void add(node *&o, node *lst, int l, int r, int p) { o = new node(), *o = *lst, o->num++; if(l == r) return; int mid = (l + r) >> 1; if(p <= mid) add(o->ch[0], lst->ch[0], l, mid, p); else add(o->ch[1], lst->ch[1], mid + 1, r, p); } int getnum(node *x, node *y, int l, int r, int ql, int qr) { if(x->num == y->num) return 0; if(ql <= l && r <= qr) return y->num - x->num; int mid = (l + r) >> 1; int cnt = 0; if(ql <= mid) cnt += getnum(x->ch[0], y->ch[0], l, mid, ql, qr); if(qr > mid) cnt += getnum(x->ch[1], y->ch[1], mid + 1, r, ql, qr); return cnt; } int getans(int l, int r) { if(l >= r) return 0; if(a[l] < a[r]) return 0; return getnum(root[l - 1], root[r], 1, n, a[r] + 1, a[l] - 1); } void cdq(int l, int r, int a, int b) { if(a > b) return; if(l > r) return; if(l == r) { for(int i = a; i <= b; i++) { int now = getans(st1[l], st2[i]); if(now > ans[i]) ans[i] = now, to[i] = l; } return; } int mid = (a + b) >> 1; //printf("now is work[%d, %d, %d, %d]\n", l, r, a, b); ans[mid] = 0; for(int i = l; i <= r; i++) { int now = getans(st1[i], st2[mid]); if(now >= ans[mid]) ans[mid] = now, to[mid] = i; } cdq(l, to[mid], a, mid - 1); cdq(to[mid], r, mid + 1, b); } void predoit() { s.resize(n); init(); for(int i = 1; i <= n; i++) add(root[i], root[i - 1], 1, n, a[i] = in()); //st1 can be left //st2 can be right for(int i = 1; i <= n; i++) { while(top2 && a[i] < a[st2[top2]]) top2--; if(a[i] > a[st1[top1]]) st1[++top1] = i; st2[++top2] = i; } int max = 0, l = 0, r = 0; cdq(1, top1, 1, top2); for(int i = 1; i <= top2; i++) if(max < ans[i]) max = ans[i], l = st1[to[i]], r = st2[i]; std::swap(a[l], a[r]); if(l ^ r) tot++; } void getans() { for(int i = n; i >= 1; i--) { tot += s.query(a[i]); s.add(a[i]); } printf("%lld\n", tot); } int main() { n = in(); predoit(); getans(); return 0; }