洛谷 P9683 A Certain Forbidden Index 题解
填坑。提供一个相对好写的做法。
考虑把一堆不交的区间绑在一起问(即先询问这些区间的并,判断答案是否在里面):对于一个区间,能与它绑在一起的区间最多只有
于是我们想到一个比较原始的做法:因为
注:对于这种做法,如果对块内相邻元素连边,在整棵线段树上这些边就可以构成很多交叉的 X 型(读者可以自行理解一下)。
但是此时答案还不是最优的。考虑是否可以合并一些区间:我们观察到,对于一些左 / 右端点在正中间的作为右 / 左儿子进行处理的区间,是可以将它所在的块和同一层那个唯一可以合并的区间所在的块合并的。例如,
事实上我们可以观察到,对于合并同一层的这种情况,我们只需处理
询问块内元素的过程可以不用像其他题解一样用二分,因为我们观察到如果一个块的大小如果不超过
如果不放心可以把所有块全部预处理出来然后按照块的大小降序排序询问。但是这个做法会在
示例代码(
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
int query(int,int);
pii solve(int k){
vector<tuple<int,int,int,int> > q;
function<void(int,int,int)> dfs=[&](int l,int r,int w){
if(l==r)return;
q.emplace_back(l,l+r>>1,w-1,0);
q.emplace_back(l+r+1>>1,r,w-1,1);
dfs(l,l+r>>1,w-1);
dfs(l+r+1>>1,r,w-1);
}; // 按照 BFS 序处理出所有区间遍历顺序,0/1 表示左 / 右儿子
dfs(1,1<<k,k);
set<pii> t; t.emplace(1,1<<k);
vector<tuple<vector<pii>,int,int,int,int> > s;
for(auto [l,r,w,b]:q){
if(t.find(make_pair(l,r))==t.end()){
vector<pii> v; int p=(b?l:r);
for(int i=w-1;i>=0;i--){
v.emplace_back(b?p-(1<<i):p+1,b?p-1:p+(1<<i));
b?p-=(1<<i):p+=(1<<i);
} // 处理块
if(b){
bool b2=false;
if(r<(1<<k)){
b2=true,p=r;
for(int i=w;i>=0;i--){
v.emplace_back(p+1,p+(1<<i));
p+=(1<<i);
}
} // 可以与同一层的合并
if(query(l-(1<<w)+1,b2?(r<<1)-l+(1<<w):r)){
// 询问是否在整个区间,下同
for(auto [l1,r1]:v)
if(query(l1,r1))return make_pair(l1,r1);
// 询问单个区间,下同
return make_pair(l,r);
// 不在上述区间就是最后一个,下同
}
}
else if(query(l,r+(1<<w)-1)){
for(auto [l1,r1]:v)
if(query(l1,r1))return make_pair(l1,r1);
return make_pair(l,r);
}
for(auto [l1,r1]:v)t.emplace(l1,r1);
t.emplace(l,r); // 使用 std::set 打标记
}
}
return make_pair(1,1<<k); // 都不是上述区间
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理