CF2163D Diadrash 解题报告
简单 adhoc。
题意就是,有你个你不知道的排列,然后有一堆区间 \(seg\)。每次你可以询问任意 \(\text{MEX}(l, r)\)。然后请在 \(30\) 次查询里求出 \(\max_{s \in seg}(\text{MEX}(s_l, s_r))\)。排列长度不大于 \(5 \cdot 10^4\),\(|seg| \le 3 \cdot 10^5\)。
首先我们想求的是 \(\max\),稍加思索其实可以发现,如果 \(\exists u, v \in seg\) 并且 \(u \subset v\),那么 \(\text{MEX}(u_l, u_r)\) 其实不可能是最优解。我们可以只保留区间 \(v\)。
这样,将剩余区间按照 \(l\) 排序后,\(r\) 其实也是单调增的,并且区间最多有 \(n\) 个。
然后如果你参加过 GDOI2026,你就知道有一个转化,叫做「在一个排列中,\(\text{MEX}(l, r) = \min(\text{premin}(l - 1), \text{sufmin}(r + 1))\)」。
那么因为 \(l\) 单调递增,那么 \(\text{premin}(l - 1)\) 单调不增。同理,\(\text{sufmin}(r + 1)\) 单调不降。
那么 \(f(i) = \text{MEX}(l_i, r_i)\) 其实是个单峰函数。这个是可以三分的。当然,为了方便,我们可以直接二分找到最后一个 \(i\),使得 \(\text{premin}(l_i - 1) \ge \text{sufmin}(r_i + 1)\)。显然询问每一个区间的 \(\text{MEX}\) 需要询问两次,那么总询问次数就是 \(2 \lceil \log_{2} n \rceil\) 的。做完了。
code by DeepSeek:
#include <bits/stdc++.h>
#define int long long
#define Misaka namespace
#define Network std
using Misaka Network;
int ask(int l, int r){
cout << "? " << l << ' ' << r << endl;
int x; cin >> x;
return x;
}
int solve_core(int n, vector<pair<int,int>> iv){
// 去掉被包含的区间(l 升序、r 降序,保留 r 严格递增的极大区间)
sort(iv.begin(), iv.end(), [](const auto& a, const auto& b){
if(a.first != b.first) return a.first < b.first;
return a.second > b.second;
});
vector<pair<int,int>> pr;
int maxr = 0;
for(auto [l, r] : iv)
if(r > maxr){ pr.push_back({l, r}); maxr = r; }
int k = (int)pr.size();
// a_j = MEX([1,r_j]) 单调不减,b_j = MEX([l_j,n]) 单调不增,MEX([l_j,r_j]) = min(a_j,b_j)
// min(a_j,b_j) 单峰,峰值在第一个 a_j >= b_j 处,答案 = max(a_{j*-1}, b_{j*})
vector<int> aval(k + 2, -1), bval(k + 2, -1);
auto probe = [&](int j){
if(aval[j] != -1) return;
aval[j] = ask(1, pr[j-1].second);
bval[j] = ask(pr[j-1].first, n);
};
int l = 0, r = k + 1; // P(0)=false, P(k+1)=true
while(l + 1 < r){
int mid = (l + r) >> 1;
probe(mid);
if(aval[mid] >= bval[mid]) r = mid;
else l = mid;
}
int ans = 0;
if(l >= 1){
if(aval[l] == -1) probe(l);
ans = max(ans, aval[l]);
}
if(r <= k){
if(bval[r] == -1) probe(r);
ans = max(ans, bval[r]);
}
return ans;
}
signed main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int t; cin >> t;
while(t--){
int n, q; cin >> n >> q;
vector<pair<int,int>> iv(q);
for(int i = 0; i < q; i++) cin >> iv[i].first >> iv[i].second;
int ans = solve_core(n, iv);
cout << "! " << ans << endl;
}
return 0;
}

浙公网安备 33010602011771号