【题解】FARIO2019-Nav
交互题,给定一张 \(n\) 个点无向图,进行 \(k\) 局游戏,每局先初始化一个点 \(x\),选手每次询问一个点 \(u\),返回 \(u\) 到 \(x\) 某条最短路上的某条边。需要在 \(9\) 次操作内求出点 \(x\)。\(n\le 300,k\le 750\)。
对于一条链的情况,可以直接二分。一棵树的情况可以直接点分治。
那么对于图的情况,我们求出每个点出发的最短路图,对这个 DAG 求出最劣情况下能排除多少个点,然后取最优的 DAG 分治。
结论,每次取最优的 DAG,决策集合至少减半。还不太会证明。
先 Floyd 求任意两点最短路,每次询问及时更新决策集合与 DAG。复杂度是 \(\mathcal{O}(n^3 + k(n^2 + m\log n))\)。
int n, d[N][N];
int nav(int);
vector<int>e[N];
vector<Pr>o;
void init(int subtask, int n_, int m, std::vector<int> A, std::vector<int> B) {
n = n_; memset(d, 0x3f, sizeof(d));
o.resize(m);
rep(i, 0, m - 1){
A[i] ++, B[i] ++, o[i] = {A[i], B[i]};
d[A[i]][B[i]] = d[B[i]][A[i]] = 1;
e[A[i]].pb(B[i]), e[B[i]].pb(A[i]);
}
rp(i, n)d[i][i] = 0;
rp(k, n)rp(i, n)rp(j, n)cmn(d[i][j], d[i][k] + d[k][j]);
}
vector<int>c;
int calc(){
if(si(c) == 1)return c[0] - 1;
int mn = inf, u = ~0;
go(x, c){
int mx = 0;
go(y, e[x]){
int sum = 0;
go(z, c)sum += d[y][z] + 1 == d[x][z];
cmx(mx, sum);
}
if(mx < mn)mn = mx, u = x;
}
assert(~u);
int p = nav(u - 1);
if(-1 == p)return u - 1;
int x = o[p].fi, y = o[p].se;
if(d[u][x] > d[u][y])swap(x, y);
vector<int>res;
go(x, c)if(d[u][y] + d[y][x] == d[u][x])res.pb(x);
c = res; return calc();
}
int findPrime() {
//rp(i, n){rp(j, n)printf("%d ", d[i][j]); el;}
c.clear();
rp(i, n)c.pb(i);
return calc();
}