2026.8 - 做题记录与方法总结
2026.8 - 做题记录与方法总结
上个月打了几局 ABC,CF,发现本人从菜鸟退化成区了(汗
2026.8.3
用树状数组维护前缀线段树,通过两个临时数组来走所需的 \(\log\) 级 线段树,通过查询时遍历所需的线段树来确定答案。
线段树部分:
namespace tree{
int ls[N<<7],rs[N<<7],sum[N<<7],tot;
#define mid ((pl + pr) >> 1)
void push_up(int p) {
sum[p] = sum[ls[p]] + sum[rs[p]];
}
void edit(int &p,int pl,int pr,int k,int d) {
if(!p) p = ++tot;
if(pl == pr) {
sum[p] += d;
return;
}
if(k <= mid) edit(ls[p],pl,mid,k,d);
else edit(rs[p],mid+1,pr,k,d);
push_up(p);
}
int query_num(int pl,int pr,int k) {
if(pl == pr) return pl;
int res = 0;
for(int i = 1;i<=cnt;i++) res += sum[ls[tem[i]]];
for(int i = 1;i<=num;i++) res -= sum[ls[tmp[i]]];
if(k <= res) {
for(int i = 1;i<=cnt;i++) tem[i] = ls[tem[i]];
for(int i = 1;i<=num;i++) tmp[i] = ls[tmp[i]];
return query_num(pl,mid,k);
}else{
for(int i = 1;i<=cnt;i++) tem[i] = rs[tem[i]];
for(int i = 1;i<=num;i++) tmp[i] = rs[tmp[i]];
return query_num(mid+1,pr,k - res);
}
}
int query_rank(int pl,int pr,int k) {
if(pl == pr) return 0;
int res = 0;
if(k <= mid) {
for(int i = 1;i<=cnt;i++) tem[i] = ls[tem[i]];
for(int i = 1;i<=num;i++) tmp[i] = ls[tmp[i]];
return query_rank(pl,mid,k);
}else {
for(int i = 1;i<=cnt;i++) res += sum[ls[tem[i]]],tem[i] = rs[tem[i]];
for(int i = 1;i<=num;i++) res -= sum[ls[tmp[i]]],tmp[i] = rs[tmp[i]];
return res + query_rank(mid + 1,pr,k);
}
}
}
其中 \(cnt,num\) 为两类前缀线段树(\([1,R]\) 和 \([1,L-1]\))的个数,每次进查询都做遍历,相当于在合并树上做操作。
树状数组操作:
void add(int x,int d) {
for(int i = x;i<=n;i += lowbit(i))
tree::edit(rt[i],1,len,a[x],d);
}
在树状数组的每个遍历节点上做权值线段树的 \(+1\) 操作。
数据结构外操作:
int find_num(int l,int r,int k) {
cnt = num = 0;
for(int i = r;i;i -= lowbit(i))
tem[++cnt] = rt[i];
for(int i = l - 1;i;i -= lowbit(i))
tmp[++num] = rt[i];
return tree::query_num(1,len,k);
}
int find_rank(int l,int r,int k) {
cnt = num = 0;
for(int i = r;i;i -= lowbit(i))
tem[++cnt] = rt[i];
for(int i = l - 1;i;i -= lowbit(i))
tmp[++num] = rt[i];
return tree::query_rank(1,len,k) + 1;
}
每次清空 \(cnt,num\) 来跑两个前缀线段树(\(query_rank\) 查小于部分个数)
int find_pre(int l,int r,int k) {
int rnk = find_rank(l,r,k) - 1;
if(rnk == 0) return 0;
return find_num(l,r,rnk);
}
int find_suc(int l,int r,int k){
if(k == len) return len + 1;
int rnk = find_rank(l,r,k + 1);
if(rnk == r - l + 2) return len + 1;
return find_num(l,r,rnk);
}
查前驱和后继部分,小心上下界。
主函数要主要离散化,尽可能减少内存使用!
询问内容也要离散化!
signed main() {
n = rd(),m = rd();
for(int i = 1;i<=n;i++) a[i] = rd();
for(int i = 1;i<=n;i++) hsh[++len] = a[i];
for(int i = 1;i<=m;i++) {
q[i][0] = rd();
q[i][1] = rd();
q[i][2] = rd();
if(q[i][0] ^ 3) q[i][3] = rd();
else hsh[++len] = q[i][2];
if(q[i][0] == 1 || q[i][0] == 4 || q[i][0] == 5) hsh[++len] = q[i][3];
}
sort(hsh + 1,hsh + len + 1);
len = unique(hsh + 1,hsh + len + 1) - hsh - 1;
hsh[0] = -inf;hsh[len + 1] = inf;
for(int i = 1;i<=n;i++){
a[i] = lower_bound(hsh + 1,hsh + len + 1,a[i]) - hsh;
add(i,1);
}
for(int i = 1;i<=m;i++){
int opt = q[i][0];
switch (opt)
{
case 1:
q[i][3] = lower_bound(hsh + 1,hsh + len + 1,q[i][3]) - hsh;
wt(find_rank(q[i][1],q[i][2],q[i][3]));putchar('\n');
break;
case 2:
wt(hsh[find_num(q[i][1],q[i][2],q[i][3])]);
putchar('\n');
break;
case 3:
add(q[i][1],-1);
a[q[i][1]] = lower_bound(hsh + 1,hsh + len + 1,q[i][2]) - hsh;
add(q[i][1],1);
break;
case 4:
q[i][3] = lower_bound(hsh + 1,hsh + len + 1,q[i][3]) - hsh;
wt(hsh[find_pre(q[i][1],q[i][2],q[i][3])]);putchar('\n');
break;
case 5:
q[i][3] = lower_bound(hsh + 1,hsh + len + 1,q[i][3]) - hsh;
wt(hsh[find_suc(q[i][1],q[i][2],q[i][3])]);putchar('\n');
break;
default:
cerr<<"Error";
return 0;
break;
}
}
return 0;
}

浙公网安备 33010602011771号