[NOI Online 提高組]氣泡排序
阿新 • • 發佈:2020-08-09
題目
洛谷等許多 \(OJ\) 都有
思路
考試題,今日無意又做了一次
然後發現自己讀錯題了······
其實詢問時只要 \(k\) 輪排序後的逆序對個數並不需要真的對序列進行更改
很顯然 \(k\) 輪操作後每一個位置產生逆序對個數比 \(k\) 小的都沒了,比 \(k\) 大的都減了 \(k\)
那麼我們只要求每一個位置產生逆序對個數比 \(k\) 大的所有的和減去他們的個數乘 \(k\) 就好了
所以只要對交換時的兩個數進行分類討論,分別算下他們交換後對逆序對個數的影響就行了
開樹狀陣列維護,一個記貢獻的和,一個記個數(權值為下標)
\(Code\)
#include<cstdio> #include<iostream> #include<cstring> #define LL long long using namespace std; const int N = 2e5 + 5; int n , m , p[N] , num[N]; struct BIT{ LL c[N]; int lowbit(int x){return x & (-x);} void add(int x , int v){for(; x <= n; x += lowbit(x)) c[x] += v;} LL query(int x) { LL res = 0; for(; x; x -= lowbit(x)) res += c[x]; return res; } }a , b; int main() { scanf("%d%d" , &n , &m); for(register int i = 1; i <= n; i++) { scanf("%d" , &p[i]); num[i] = a.query(n) - a.query(p[i]); a.add(p[i] , 1); } memset(a.c , 0 , sizeof a.c); for(register int i = 1; i <= n; i++) if (num[i] != 0) a.add(num[i] , num[i]) , b.add(num[i] , 1); int t , k; for(; m; m--) { scanf("%d%d" , &t , &k); if (t == 1) { if (p[k] > p[k + 1]) { if (num[k + 1] != 0) a.add(num[k + 1] , -num[k + 1]) , b.add(num[k + 1] , -1) , num[k + 1]--; if (num[k + 1] != 0) a.add(num[k + 1] , num[k + 1]) , b.add(num[k + 1] , 1); } else{ if (num[k] != 0) a.add(num[k] , -num[k]) , b.add(num[k] , -1); num[k]++ , a.add(num[k] , num[k]) , b.add(num[k] , 1); } swap(p[k] , p[k + 1]) , swap(num[k] , num[k + 1]); } else { if (k >= n) { printf("0\n"); continue; } printf("%lld\n" , a.query(n) - a.query(k) - (b.query(n) - b.query(k)) * k); } } }