BZOJ-3289-Mato的文件管理-莫队+树状数组
生活随笔
收集整理的這篇文章主要介紹了
BZOJ-3289-Mato的文件管理-莫队+树状数组
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
描述
給定一段 n(n≤50000) 個數的序列, m(m≤50000) 次詢問 [L, R] 區間內相鄰元素兩兩交換使得序列不降的最少次數.
分析
- 首先轉化為一個逆序對的問題, 最少交換的次數就是逆序對的個數. 后面的證明說的不嚴謹甚至可能是錯的, 不過可以作為啟發和參考吧 : 對于序列中的一個元素x, 其后面比它小的元素有c個, 討論它后面第一個元素y的值, 如果y比x小, 那么需要交換x和y使得序列不降, 這樣操作個數增加的實際上就是一對逆序對, 然后x到了y的位置繼續 ; 如果y大于或等于x, 那么需要先把y移動到它應該去的位置, 再移動x, 這樣增加的就是以x和y開頭的逆序對數之和.
- 然后用莫隊算法離線統計. 需要解決的問題就是如何統計在一段已經統計出逆序對個數的序列的開頭或者結尾添加或刪除一個元素后對原逆序對個數的影響. 可以用樹狀數組記錄離散后的權值在當前序列[A[L], A[R]]中的出現次數的前綴和.
-
①在一列數的后面添加一個數,逆序對數會增加 數列中比它大的數的個數。
②在一列數的后面刪除一個數,逆序對數會減少 數列中比它大的數的個數。
③在一列數的前面添加一個數,逆序對數會增加 數列中比它小的數的個數。
④在一列數的前面刪除一個數,逆序對數會減少 數列中比它小的數的個數。
-
from :?AutSky_JadeK
- 用樹狀數組記錄后用莫隊算法統計.
#include #include #include using namespace std;const int maxn = 50000 + 10;int tot, block; int A[maxn], T[maxn]; int c[maxn]; int ans[maxn];struct Question {int L, R, id;bool operator < (const Question& rhs) const {if(L/block != rhs.L/block) return L < rhs.L;return R < rhs.R;} } questions[maxn];#define q questions[i]void modify(int x, int d) {for(; x <= tot; x += x&-x) c[x] += d; }int query(int x) {int ret = 0;for(; x > 0; x -= x&-x) ret += c[x];return ret; }int main() {int n, m;scanf("%d", &n);block = (int)(sqrt(n) + 0.5);for(int i = 1; i <= n; i++)scanf("%d", &A[i]), T[i] = A[i];sort(T+1, T+n+1);tot = unique(T+1, T+n+1)-T - 1;for(int i = 1; i <= n; i++)A[i] = lower_bound(T+1, T+tot+1, A[i])-T;scanf("%d", &m);for(int i = 1; i <= m; i++)scanf("%d %d", &q.L, &q.R), q.id = i;sort(questions+1, questions+m+1);int L = 1, R = 0, cur = 0;for(int i = 1; i <= m; i++) {while(L > q.L) cur += query(A[--L]-1), modify(A[L], 1);while(R < q.R) cur += query(tot)-query(A[++R]), modify(A[R], 1);while(L < q.L) modify(A[L], -1), cur -= query(A[L++]-1);while(R > q.R) modify(A[R], -1), cur -= query(tot)-query(A[R--]);ans[q.id] = cur;}for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);return 0; }
總結
以上是生活随笔為你收集整理的BZOJ-3289-Mato的文件管理-莫队+树状数组的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BZOJ-2588-Count-on-a
- 下一篇: CODEVS-1758-维护数列-NOI