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;
}
posted @ 2025-07-26 09:07  Tmbcan  阅读(17)  评论(0)    收藏  举报