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号