CF2240E 思路分享(构造,树状数组上二分)

https://codeforces.com/contest/2240/problem/E

题意

对于长度为 \(n\) 的排列 \(p\),定义 \(s_i\)\(p[1\cdots i]\) 的逆序对数量.

给定 \(n\) 个二元组 \((c_i,x_i)\),若 \(c_i='p'\),表示 \(p_i = x\);若 \(c_i = 's'\),表示 \(s_i = x\).

构造出满足条件的一个排列,保证一定存在.

\(1\le n \le 2\cdot 10^5\).

思路

对于 \(c_i = 'p'\),直接填 \(x\).

对于 \(c_i = 's'\)\(s_i\) 只与前缀有关,考虑从后往前构造.

用树状数组维护未被使用的数集 \(S\).

处理思路是根据单独 \(i\) 位置产生的贡献确定 \(p_i\),这可以在树状数组上二分实现.

初始贡献为 \(cur=x_i\),首先往前找到第一个 \(c_j = 's'\) 的位置,删掉 \((j,i)\) 范围内的贡献,减去 \(x_j\).

具体来说,倒序遍历 \(k \in (j,i)\),将 \(cur\) 减去 \(S\) 中大于 \(p_k\) 的数量,随后删掉 \(p_k\).

这样多减了 \(p_k\) 与即将被选作 \(p_i\) 的贡献,因为满足 \(p_k \lt p_i\),将 \((j,i)\) 内所有数重新加回 \(S\),权值为 \(2\),可以用这样的技巧抵消.

最后在树状数组上二分求第 \(i-cur\) 小的数即可.

\(k\) 小本质是第一个满足前缀和大于等于 \(k\) 的位置,而树状数组上二分只能求最后一个满足条件的位置,因此求出最后一个满足:\(pos\) 的前缀和 \(\lt k\) 的位置,\(pos+1\) 即为第 \(k\) 小.

时间复杂度 \(\mathcal{O}(n\log n)\).

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

class fenwick{
public:
	int n;
	vector<ll> a;

	fenwick(int _n){
		n = _n;
		a.assign(n+1,0);
	}

	void build(vector<ll>& b){
		a = b;
		for (int i=1;i<=n;i++){
			int j = i+(i&-i);
			if (j<=n){
				a[j] += a[i];
			}
		}
	}

	void update(int x,ll val){
		if (x<1 || x>n) return;
		for (int i=x;i<=n;i+=i&-i){
			a[i] += val;
		}
	}

	ll query(int x){
		if (x<1 || x>n) return 0; 
		ll res = 0;
		for (int i=x;i>=1;i-=i&-i){
			res += a[i];
		}
		return res;
	}

	ll query_range(int l,int r){
		if (l<1 || l>n || r<1 || r>n || l>r) return 0;
		return query(r)-query(l-1); 
	}

	int kth(int k){
		int pos = 0;
		int sum = 0;
		for (int i=20;i>=0;i--){
			if (pos+(1<<i)<=n && sum+a[pos+(1<<i)]<k){
				sum += a[pos+(1<<i)];
				pos += 1<<i;
			}
		}
		return pos+1;
	}
};

void solve(){
	int n;
	cin >> n;

	vector<int> p(n+1);
	vector<pair<char,ll>> Q(n+1);
	for (int i=1;i<=n;i++){
		cin >> Q[i].first >> Q[i].second;
	}
	Q[0] = {'s',0};

	vector<ll> temp(n+1,1);
	fenwick fw(n);
	fw.build(temp);

	int i = n;
	while (i>=1){
		if (Q[i].first=='p'){
			p[i] = Q[i].second;
			fw.update(p[i],-1);
			i--;
			continue;
		}

		ll cur = Q[i].second;
		int j = i-1;
		while (Q[j].first=='p'){
			cur -= fw.query_range(Q[j].second+1,n);
			fw.update(Q[j].second,-1);
			p[j] = Q[j].second;
			j--;
		}
		cur -= Q[j].second;

		for (int k=j+1;k<i;k++){
			fw.update(Q[k].second,2);
		}

		p[i] = fw.kth(i-cur);
		fw.update(p[i],-1);
		for (int k=j+1;k<i;k++){
			fw.update(Q[k].second,-2);
		}

		i = j;
	}	

	for (int i=1;i<=n;i++){
		cout << p[i] << ' ';		
	}
	cout << '\n';
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t = 1;
	cin >> t;
	while (t--) solve();

	return 0;
}
posted @ 2026-06-28 14:38  kzssCCC  阅读(11)  评论(0)    收藏  举报