ARC181D Prefix Bubble Sort
思路
發現如果直接維護序列的話需要支持:序列插入刪除,動態前綴最大值,然后再維護答案。
這個東西根本沒法弄。所以我們考慮逆序對的性質。
先考慮 \(\forall i,a_i=n\) 的怎么做。
發現一次操作最多使一個數向前移動一個位置,并且一共可以移動的次數,為其左側比它大的數字的個數,設為 \(c_j\)。
所以我們可以考慮維護 \(c_j\),因為其和即為逆序對個數。
每次操作會使 \(c_j\) 減一,減到零之后就對答案沒有貢獻了,所以 \(c_j\) 在時間軸上對答案的貢獻是:首項為 \(c_j\),末項為 \(0\),公差為 \(1\) 的等差數列。
現在我們考慮原問題。
因為 \(a_i\) 單調不降,所以對于 \(j \in (a_i,n]\) 的 \(c_j\),其在 \(time \in [1,i)\) 會對答案一直產生 \(c_j\) 的貢獻;在 \(time \in [i,i+c_j]\) 才會對答案產生等差數列的貢獻。
這相當于對于時間軸 \(time \in [1,i)\) 前綴加 \(c_j\),\(time \in [i,i+c_j]\) 區間加等差數列,最后前綴查詢。
線段樹或者差分維護一下就可以了。
代碼
const int N = 2e5+10;
int n,m,a[N],c[N],r[N];
ll ans,sum[N];
int tr[N];
inline void add(int x){
for(int i=x;i<=n;i+=i&(~i+1)) ++tr[i];
}
inline int query(int x){
int res = 0;
for(int i=x;i;i-=i&(~i+1)) res += tr[i];
return res;
}
int main(){
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
read(n);
for(int i=1;i<=n;++i){
read(a[i]);
c[i] = query(n)-query(a[i]);
ans += c[i];
add(a[i]);
}
read(m);
for(int i=1;i<=m;++i) read(r[i]);
for(int i=1;i<=n;++i){
int l = lower_bound(r+1,r+1+m,i)-r;
++sum[l];
--sum[Min(l+c[i],m+1)];
}
for(int i=1;i<=m;++i) sum[i] += sum[i-1];
for(int i=1;i<=m;++i){
ans -= sum[i];
printf("%lld\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}

浙公網安備 33010602011771號