Loading

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;
}
posted @ 2026-08-26 12:37  Trent900  阅读(17)  评论(0)    收藏  举报