P13614 [IOI 2018] highway 高速公路收费

首先可以先二分出一条边 \(e\),满足把 \(w_{0\sim e-1}\) 变成 \(B\) 后最短路不变,且再把 \(w_e\) 变为 \(B\) 后最短路会变。现在我们可以认为现在 \(S\)\(T\) 之间的最短路都要经过 \(e\)

此时可以认为 \(S\)\(T\)\(e\) 分割,是相对独立的。对于树的部分分,我们可以分开查询两边的深度,但是图上因为存在一些其它的边,所以不太能去问出深度,我们需要一个好一点的办法。

对于最短路的刻画还有一个想法是 BFS,事实上我们也可以再 BFS 序上二分。每次我们把 BFS 序上一段后缀的点的入边设为 \(B\),然后就可以判断 \(S\)\(T\) 在哪个部分。

如果直接对整个点集二分可能会多一两次操作。事实上我们只要对于两部分分别二分即可,这里划分部分可以去比对与 \(e\) 两端点的距离。

那么假设两部分大小分别是 \(a,b\),次数就是 \(1+\log n+\log a+\log b\),上界为 \(50\) 次。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int> ;
template <typename T> void Chkmin(T &x, T y) { x = min(x, y); }
template <typename T> void Chkmax(T &x, T y) { x = max(x, y); }
long long ask(const vector<int> &w); 
void answer(int s, int t);

const int kN = 9e4 + 5;
int n, m, A, B;
ll dis;
int disu[kN], disv[kN];
vector<pii> g[kN];

void BFS(int s, int *dis, vector<int> &ord) {
  static bool vis[kN];
  memset(vis, 0, sizeof(vis));
  queue<int> q;
  q.push(s), vis[s] = 1;
  while(q.size()) {
    int x = q.front(); q.pop();
    ord.push_back(x);
    for(pii k : g[x]) {
      int to = k.first;
      if(vis[to]) continue;
      q.push(to);
      vis[to] = 1;
      dis[to] = dis[x] + 1;
    }
  }
}
int Find(int e, vector<int> ord) {
  static int rk[kN];
  memset(rk, 0, sizeof(rk));
  int sz = ord.size();
  for(int i = 0; i < sz; i++) rk[ord[i]] = i + 1;
  int l = -1, r = sz;
  while(l + 1 < r) {
    int mid = (l + r) >> 1;
    vector<int> f (m, 0);
    fill(f.begin(), f.begin() + e, 1);
    for(int i = mid; i < sz; i++) {
      int p = ord[i];
      for(pii k : g[p]) {
        int to = k.first;
        int eid = k.second;
        if(rk[p] > rk[to]) f[eid] = 1;
      }
    }
    (ask(f) != dis) ? (l = mid) : (r = mid);
  }
  return ord[l];
}
void find_pair(int N, vector<int> U, vector<int> V, int _A, int _B) {
  n = N;
  m = U.size();
  A = _A;
  B = _B;
  for(int i = 0; i < m; i++) {
    int u = ++U[i];
    int v = ++V[i];
    g[u].emplace_back(v, i);
    g[v].emplace_back(u, i);
  }
  dis = ask(vector<int> (m, 0));
  int l = -1, r = m;
  while(l + 1 < r) {
    int mid = (l + r) >> 1;
    vector<int> f (m, 0);
    fill(f.begin(), f.begin() + mid + 1, 1);
    (ask(f) != dis) ? (r = mid) : (l = mid);
  }
  int u = U[r], v = V[r];
  vector<int> ordu, ordv;
  BFS(u, disu, ordu);
  BFS(v, disv, ordv);
  vector<int> idu, idv;
  for(int x : ordu) {
    if(disu[x] < disv[x]) idu.push_back(x);
  }
  for(int x : ordv) {
    if(disu[x] > disv[x]) idv.push_back(x);
  }
  int x = Find(r, idu);
  int y = Find(r, idv);
  answer(x - 1, y - 1);
}
posted @ 2025-08-20 21:47  CJzdc  阅读(2)  评论(0)    收藏  举报