P13614 [IOI 2018] highway 高速公路收费
Genius_Star · · 题解
很牛的题。
思路:
显然你可以将所有边赋值为
依次赋值判断太慢了,显然满足单调性,可以二分边集前缀去找,要
可以证明
于是对于左右两个部分,分别建出以
先求在左边的起点,右边类似,容易想到二分一个 bfs 序的前缀,将这个前缀上的点到上一层的边赋值为
于是询问次数是
思路:
#include<bits/stdc++.h>
#define ls(k) k << 1
#define rs(k) k << 1 | 1
#define lowbit(x) x & (-x)
#define fi first
#define se second
#define popcnt(x) __builtin_popcount(x)
#define open(s1, s2) freopen(s1, "r", stdin), freopen(s2, "w", stdout);
using namespace std;
typedef __int128 __;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
inline ll read(){
ll x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')
f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
//void answer(int s, int t){
// printf("!ans %d %d\n", s, t);
//}
//long long ask(const vector<int> &w){
// for(int i = 0; i < (int)w.size(); ++i)
// putchar(w[i] + '0');
// putchar('\n');
// return read();
//}
ll ask(const vector<int> &w);
void answer(int s, int t);
const int N = 1.3e5 + 10;
struct edge{
int u, v;
}e[N];
int n, m, a, b, edg, cnt;
int id[N], now[N], dep[N];
bool used[N];
ll dis;
vector<int> vis, fid[N];
vector<pair<int, int>> E[N];
inline void get(int x, int y){
id[x] = 1, id[y] = 2;
queue<int> q;
q.push(x);
q.push(y);
used[x] = used[y] = 1;
while(!q.empty()){
int u = q.front();
q.pop();
for(auto t : E[u]){
int v = t.fi;
if(!used[v]){
used[v] = 1;
id[v] = id[u];
q.push(v);
}
}
}
}
inline int bfs(int u, int x){
// cerr << "id: " << x << '\n';
for(int i = 1; i <= n; ++i)
fid[i].clear(), dep[i] = 1e9;
for(int i = 0; i < m; ++i)
vis[i] = 0;
cnt = 0;
queue<int> q;
dep[u] = 0;
q.push(u);
while(!q.empty()){
int u = q.front();
q.pop();
// cerr << "node: " << u << '\n';
now[++cnt] = u;
for(auto t : E[u]){
int v = t.fi, w = t.se;
if(id[v] == x){
if(dep[v] > dep[u] + 1){
dep[v] = dep[u] + 1;
q.push(v);
}
if(dep[v] >= dep[u])
fid[v].push_back(w);
}
}
}
for(auto i = 1; i <= cnt; ++i)
for(auto t : E[now[i]])
if(id[t.fi] != x && t.se != edg)
vis[t.se] = 1;
// cerr << '\n';
for(int i = 2; i <= cnt; ++i)
for(auto v : fid[now[i]])
vis[v] = 1;
// cerr << '\n';
int l = 1, r = cnt, pos = 0;
while(l <= r){
int mid = (l + r) >> 1;
for(int i = 2; i <= mid; ++i)
for(auto v : fid[now[i]])
vis[v] = 0;
if(ask(vis) == dis){
pos = mid;
r = mid - 1;
}
else
l = mid + 1;
for(int i = 2; i <= mid; ++i)
for(auto v : fid[now[i]])
vis[v] = 1;
}
return now[pos];
}
inline void solve(){
for(int i = 0; i < m; ++i)
vis.push_back(0);
dis = ask(vis);
int l = 1, r = m, pos = 0;
while(l <= r){
int mid = (l + r) >> 1;
for(int i = 0; i < mid; ++i)
vis[i] = 1;
if(ask(vis) != dis){
pos = mid;
r = mid - 1;
}
else
l = mid + 1;
for(int i = 0; i < mid; ++i)
vis[i] = 0;
}
int x = e[pos].u, y = e[pos].v;
edg = pos - 1;
// cerr << edg << '\n';
get(x, y);
// cerr << "edge: " << x << ' ' << y << '\n';
int s = bfs(x, 1);
int t = bfs(y, 2);
answer(s - 1, t - 1);
}
void find_pair(int N, vector<int> U, vector<int> V, int A, int B){
n = N, a = A, b = B, m = U.size();
for(int i = 0; i < m; ++i){
++U[i], ++V[i];
e[i + 1] = {U[i], V[i]};
E[U[i]].push_back({V[i], i});
E[V[i]].push_back({U[i], i});
}
solve();
}
//int main(){
// int n = read(), m = read(), a = read(), b = read(), s = read(), t = read();
// vector<int> U, V;
// for(int u, v, i = 0; i < m; ++i){
// u = read(), v = read();
// U.push_back(u);
// V.push_back(v);
// }
// find_pair(n, U, V, a, b);
// return 0;
//}