![9b44905c4fd463afcfee852d9467d03]()
#define ll long long // 定义long long为ll,简化代码书写
using namespace std;
// 线段树类:用于维护元素频率,支持更新、第k小查询和区间和查询
struct SegTree{
int m; // 离散化后的值域大小(不同元素的数量)
vector<int>tree_sum; // 线段树节点数组,存储区间内元素的总频率
// 构造函数:初始化线段树大小
SegTree(int size){
m = size; // 记录离散化后的值域大小
// 线段树数组大小开4倍,确保能容纳所有节点(避免越界)
tree_sum.assign(4 * size, 0);
}
// 递归实现单点更新:更新指定位置的频率(delta为+1或-1)
// id:当前节点编号;l/r:当前节点覆盖的区间;pos:要更新的位置;delta:变化量
void update(int id, int l, int r, int pos, int delta){
if(l == r){ // 到达叶子节点(对应单个离散化后的值)
tree_sum[id] += delta; // 更新频率
return;
}
int mid = (l + r) / 2; // 划分左右子区间
if(pos <= mid){ // 目标位置在左子区间,递归更新左子树
update(id << 1, l, mid, pos, delta); // id<<1等价于2*id(左子节点编号)
} else { // 目标位置在右子区间,递归更新右子树
update(id << 1 | 1, mid + 1, r, pos, delta); // id<<1|1等价于2*id+1(右子节点编号)
}
// 回溯时更新当前节点的总频率(左右子树频率之和)
tree_sum[id] = tree_sum[id << 1] + tree_sum[id << 1 | 1];
}
// 对外接口:简化更新调用(从根节点开始)
void update(int pos, int delta){
update(1, 0, m - 1, pos, delta); // 根节点编号为1,覆盖区间[0, m-1]
}
// 递归实现第k小查询:返回第k小元素的离散化索引
// id:当前节点编号;l/r:当前节点覆盖的区间;k:目标第k小(0-based)
int kth(int id, int l, int r, int k){
if(l == r){ // 到达叶子节点,返回当前位置(即第k小元素的索引)
return l;
}
int mid = (l + r) / 2; // 划分左右子区间
// 左子树的频率总和为tree_sum[id<<1]
if(k < tree_sum[id << 1]){ // 第k小在左子树
return kth(id << 1, l, mid, k);
} else { // 第k小在右子树,减去左子树的频率后查询
return kth(id << 1 | 1, mid + 1, r, k - tree_sum[id << 1]);
}
}
// 对外接口:简化第k小查询调用(从根节点开始)
int kth(int k){
return kth(1, 0, m - 1, k);
}
// 递归实现区间和查询:返回[ql, qr]区间内的频率总和
// id:当前节点编号;l/r:当前节点覆盖的区间;ql/qr:查询区间
int query_sum(int id, int l, int r, int ql, int qr){
if(ql > qr) return 0; // 查询区间无效,返回0
if(ql <= l && r <= qr){ // 当前节点区间完全被查询区间包含
return tree_sum[id]; // 直接返回当前节点的总频率
}
int mid = (l + r) / 2; // 划分左右子区间
int res = 0; // 存储查询结果
if(ql <= mid){ // 左子区间与查询区间有交集,递归查询左子树
res += query_sum(id << 1, l, mid, ql, qr);
}
if(qr > mid){ // 右子区间与查询区间有交集,递归查询右子树
res += query_sum(id << 1 | 1, mid + 1, r, ql, qr);
}
return res; // 返回左右子树的频率总和
}
// 对外接口:简化区间和查询调用(从根节点开始)
int query_sum(int ql, int qr){
return query_sum(1, 0, m - 1, ql, qr);
}
};
int main(){
ios::sync_with_stdio(false); // 关闭输入输出同步,加速cin/cout
cin.tie(0);
int t; // 测试用例数量
cin >> t;
while(t--){ // 循环处理每个测试用例
int n, q; // n:初始数组长度;q:操作次数
cin >> n >> q;
vector<ll>a(n); // 存储初始数组元素
for(int i = 0; i < n; i++){
cin >> a[i];
}
// 收集所有可能出现的值(初始值+所有更新后的值),用于离散化
vector<pair<int, ll>>ops; // 存储所有操作(位置p和增量v)
vector<ll>all = a; // 初始值加入待离散化列表
vector<ll>cur = a; // 模拟数组更新过程,用于收集更新后的值
// 读取所有操作,收集更新后的值
for(int i = 0; i < q; i++){
int p; ll v; // p:位置(1-based);v:增量
cin >> p >> v;
p--; // 转换为0-based索引
ops.push_back({p, v}); // 记录操作
cur[p] += v; // 模拟更新当前位置的值
all.push_back(cur[p]); // 将更新后的值加入待离散化列表
}
// 离散化处理:排序+去重
sort(all.begin(), all.end()); // 排序所有可能的值
// 去重:删除连续重复元素,保留唯一值
all.erase(unique(all.begin(), all.end()), all.end());
int m = all.size(); // 离散化后的值域大小(不同值的数量)
// 初始化线段树,大小为m
SegTree segtree(m);
vector<ll>cur_val = a; // 当前数组的值(初始为a)
vector<int>idx(n); // 存储每个元素对应的离散化索引
// 初始化线段树:将初始元素插入线段树
for(int i = 0; i < n; i++){
// 查找当前元素在离散化列表中的索引(lower_bound返回第一个>=目标值的位置)
idx[i] = lower_bound(all.begin(), all.end(), cur_val[i]) - all.begin();
segtree.update(idx[i], 1); // 插入元素(频率+1)
}
vector<int>ans_list; // 存储每次查询的结果
// 处理每个操作
for(int i = 0; i < q; i++){
int p = ops[i].first; // 操作位置(0-based)
ll v = ops[i].second; // 增量
ll old_val = cur_val[p]; // 原数值
ll new_val = old_val + v; // 更新后的新数值
cur_val[p] = new_val; // 更新当前数组的值
// 计算原数值和新数值的离散化索引
int old_idx = idx[p]; // 原索引
// 新索引:查找新值在离散化列表中的位置
int new_idx = lower_bound(all.begin(), all.end(), new_val) - all.begin();
idx[p] = new_idx; // 更新索引记录
// 更新线段树:删除原数值(频率-1),插入新数值(频率+1)
segtree.update(old_idx, -1);
segtree.update(new_idx, 1);
// 计算阈值t:n - (n/2)(例如n=5时t=3,对应第3小元素)
int threshold = n - (n / 2);
// 查询第threshold小元素的离散化索引(注意:kth是0-based,threshold需减1)
int x_idx = segtree.kth(threshold - 1);
// 计算小于x_idx的元素总数(即[0, x_idx-1]区间的频率和)
int ans = 0;
if(x_idx > 0){ // 若x_idx>0,查询有效区间
ans = segtree.query_sum(0, x_idx - 1);
}
ans_list.push_back(ans); // 记录结果
}
// 输出所有查询结果
for(int i = 0; i < q; i++){
cout << ans_list[i] << '\n';
}
}
return 0;
}`
**注意这个题目中的数会修改,我们存下问题离线进行,将每一个修改后的可能值都存入all中,然后再离散化**