cf1705 E. Mark and Professor Koro
题意:
给定数组。对每次询问 i x
,回答把 改成 后数组的价值。这种修改是永久的。
定义数组的价值为:
每次选择数组中两个相同的数 ,把它们都删掉然后把 加入数组,进行任意次操作后数组中最大的数
思路:
维护一个位数很多的二进制数,最右边为最高位。加入数 就是让这二进制数加上一个第 位为 1,其他位为 0 的二进制数。数组的价值就是这二进制数的最高位
把 改成 就是减去 再加上
就是找位置 x 右边第一个 0 的位置 y,让第 x~y-1 位为 0、第 y 位为 1
就是找位置 x 右边第一个 1 的位置 y,让第 x~y-1 位为 1、第 y 位为 0
用set维护 “1” 区间:
const signed N = 5 + 2e5;
int n, q, a[N];
set<PII> S; //记录所有的'1'段,相邻段不会合并,比如可能同时存在{2,3}和{4,4},但似乎没关系
void add(int x) { //加法
auto it = S.upper_bound({x,INF}); //x,..,[l,r]
if(it == S.begin() || prev(it)->second < x) //x还不存在
S.insert({x,x});
else {
auto [l,r] = *prev(it); //[l,x,r]
S.erase({l,r});
if(l<=x-1) S.insert({l,x-1});
add(r+1);
}
}
void sub(int x) { //减法
auto it = S.upper_bound({x,INF}); //x,..,[l,r]
if(it == S.begin() || prev(it)->second < x) {
auto [l,r] = *it;
S.erase({l,r});
if(l+1<=r) S.insert({l+1,r});
S.insert({x,l-1});
}
else {
auto [l,r] = *prev(it); //[l,x,r]
S.erase({l,r});
if(l<=x-1) S.insert({l,x-1});
if(x+1<=r) S.insert({x+1,r});
}
}
void sol() {
cin >> n >> q;
for(int i = 1; i <= n; i++) cin >> a[i], add(a[i]);
while(q--) {
int i, x; cin >> i >> x;
sub(a[i]), add(x), a[i] = x;
cout << S.rbegin()->second << endl;
}
}
除此之外也可以权值线段树上二分(找最大值/右边第一个0或1)
发现线段树比set还慢一些,插入建树只比build建树慢一丁点
const signed N = 20 + 2e5;
int n, q, a[N], cnt[N+5]; //cnt开个2e5+log2(2e5)就行了
#define mid (l+r)/2
#define lson u*2
#define rson u*2+1
#define ls lson,l,mid
#define rs rson,mid+1,r
int sum[N*4], lzy[N*4];
void pushup(int u) {
sum[u] = sum[lson] + sum[rson];
}
void pushdn(int u, int l, int r) {
if(lzy[u]) {
sum[lson] += lzy[u] * (mid-l+1), lzy[lson] += lzy[u];
sum[rson] += lzy[u] * (r - mid), lzy[rson] += lzy[u];
lzy[u] = 0;
}
}
void build(int u, int l, int r) {
if(l == r) {
sum[u] = cnt[l];
return;
}
build(ls), build(rs);
pushup(u);
}
void add(int u, int l, int r, int x, int y, int d) {
if(x <= l && r <= y) {
sum[u] += d * (r-l+1), lzy[u] += d;
return;
}
pushdn(u, l, r);
if(x <= mid) add(ls, x, y, d);
if(y > mid) add(rs, x, y, d);
pushup(u);
}
int right_first(int u, int l, int r, int x, bool v) { //x位置右边第一个0/1,可以是x
if(r < x || sum[u] == (v^1)*(r-l+1)) return 0; //区间里一个0/1都没有
if(l == r) return l;
pushdn(u, l, r);
int left_ans = right_first(ls, x, v);
if(left_ans) return left_ans;
else return right_first(rs, x, v);
}
int tot_max(int u, int l, int r) {
if(l == r) return l;
pushdn(u, l, r);
if(sum[rson]) return tot_max(rs);
else return tot_max(ls);
}
void sol() {
cin >> n >> q;
for(int i = 1; i <= n; i++) cin >> a[i], cnt[a[i]]++;
for(int i = 1; i < N; i++) //模拟加法进位
cnt[i+1] += cnt[i] / 2, cnt[i] %= 2;
build(1,1,N); //对cnt建树
//cnt建完树就没用了
while(q--) {
int i, x; cin >> i >> x;
int y = right_first(1,1,N, a[i], 1);
if(a[i] <= y-1) add(1,1,N, a[i], y-1, 1);
add(1,1,N, y, y, -1);
a[i] = x;
y = right_first(1,1,N, a[i], 0);
if(a[i] <= y-1) add(1,1,N, a[i], y-1, -1);
add(1,1,N, y, y, 1);
cout << tot_max(1,1,N) << endl;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现