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;
}

浙公网安备 33010602011771号