【洛谷6186】[NOI Online #1 提高組] 氣泡排序(樹狀陣列)
阿新 • • 發佈:2021-01-09
- 給定一個長度為\(n\)的排列,有兩種操作:
- 交換一對相鄰的數。
- 詢問\(k\)輪氣泡排序之後的逆序對個數。
- \(n,q\le2\times 10^5\)
簡單的結論題
記錄\(c_i\)表示第\(i\)個數左側有多少比它大的數。
則一輪氣泡排序相當於是從左到右把每個\(c_i=0\)的數移到下一個\(c_i=0\)的數左邊。
除此之外,所有數左側都會有某個比它大的數移到了它的右側,因此\(c_i\)減\(1\)。
那麼對於一次詢問相當於就是求出\(\sum_{i=1}^{n}\max\{c_i-k,0\}\)。
這等同於所有大於等於\(k\)的\(c_i\)之和減去大於等於\(k\)
發現一次修改實際上只會影響到交換的這兩個位置的\(c_i\),因此就是單點修改。
那麼只要維護一個權值樹狀陣列即可。
程式碼:\(O(nlogn)\)
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 200000 #define LL long long using namespace std; int n,a[N+5],c[N+5]; struct TreeArray { LL a[N+5];I void U(RI x,CI v) {++x;W(x) a[x]+=v,x-=x&-x;}//單點修改 I LL Q(RI x,LL t=0) {++x;W(x<=n+1) t+=a[x],x+=x&-x;return t;}//字尾求和 }A,T1,T2; int main() { #define T(x,v) (T1.U(x,v),T2.U(x,v*x)) RI Qt,i;for(scanf("%d%d",&n,&Qt),i=1;i<=n;++i) scanf("%d",a+i),c[i]=A.Q(a[i]),A.U(a[i],1),T(c[i],1);//求出初始c[i] RI op,x;W(Qt--) scanf("%d%d",&op,&x),op==2?printf("%lld\n",T2.Q(x)-T1.Q(x)*x):(T(c[x],-1),T(c[x+1],-1),//操作2直接查詢;操作1先清除原貢獻 a[x]>a[x+1]&&--c[x+1],swap(a[x],a[x+1]),swap(c[x],c[x+1]),a[x]>a[x+1]&&++c[x+1],T(c[x],1),T(c[x+1],1),0);//交換,然後把貢獻加回去 return 0; }