ARC181D Prefix Bubble Sort

思路

发现如果直接维护序列的话需要支持:序列插入删除,动态前缀最大值,然后再维护答案。
这个东西根本没法弄。所以我们考虑逆序对的性质。

先考虑 的怎么做。
发现一次操作最多使一个数向前移动一个位置,并且一共可以移动的次数,为其左侧比它大的数字的个数,设为

所以我们可以考虑维护 ,因为其和即为逆序对个数。
每次操作会使 减一,减到零之后就对答案没有贡献了,所以 在时间轴上对答案的贡献是:首项为 ,末项为 ,公差为 的等差数列。

现在我们考虑原问题。
因为 单调不降,所以对于 ,其在 会对答案一直产生 的贡献;在 才会对答案产生等差数列的贡献。

这相当于对于时间轴 前缀加 区间加等差数列,最后前缀查询。

线段树或者差分维护一下就可以了。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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;
}