联训 2
作者:互联网
算个 trash round,考场觉得就 T4 还可以,结果是个暴力题……
T1 是卡牌类模拟,T2 trivial 构造,T3 不知道为啥挂了,等我找到原因再更新一发。
D. possible:给定一棵以 \(1\) 为根的带边权有根树,再给出一些带权返祖边,多次询问两点间最短距离,保证树高最多 \(20\)。
注意到两个事情,首先如果我们像点分治那样对每个子树暴力跑最短路复杂度是对的,考虑深度最高 \(20\) 即可;其次两点最短距离我们可以暴力跳两个结点的祖先然后用最短路的结果判断。
然后就做完了嘛,代码在 accoders 要开氧气才能过,很久没体验过的一遍过题……
int n, m, Q, par[100100], dep[100100];
vi<pil> adj[100100], adj2[100100];
bastr<int> sub[100100];
map<int, LL> mp[100100];
LL dis[100100];
bitset<100100> vis;
void dfs(int x, int pa) {
par[x] = pa;
dep[x] = dep[pa]+1;
for (auto const& e : adj[x]) {
if (e.first != pa) {
dfs(e.first, x);
}
}
}
int now;
void dfs2(int x) {
sub[now] += x;
dis[x] = -1, vis.reset(x);
for (auto const& e : adj[x]) {
if (e.first != par[x]) {
dfs2(e.first);
}
}
}
void work(int st) {
priority_queue<pli, vi<pli>, greater<pli>> q;
now = st;
dfs2(st);
q.emplace(dis[st] = 0, st);
while (!q.empty()) {
int x = q.top().second;
q.pop();
if (vis.test(x)) continue;
vis.set(x);
for (auto const& e : adj2[x]) {
int y = e.first;
LL w = e.second;
if (dis[y] == -1 || dis[y] > dis[x]+w) {
dis[y] = dis[x]+w;
if (!vis[y]) {
q.emplace(dis[y], y);
}
}
}
}
for (auto v : sub[st]) {
mp[st][v] = dis[v];
}
}
void dfs3(int x) {
work(x);
for (auto const& e : adj[x]) {
if (e.first != par[x]) {
dfs3(e.first);
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> Q;
for (int i=1,x,y,z; i<n; ++i) {
cin >> x >> y >> z;
adj[x].eb(y, z), adj[y].eb(x, z);
}
dfs(1, 0);
for (int i=n,x,y,z; i<=m; ++i) {
cin >> x >> y >> z;
adj2[x].eb(y, z), adj2[y].eb(x, z);
}
for (int i=1; i<=n; ++i) {
for (auto const& e : adj[i]) {
adj2[i].pb(e);
}
}
dfs3(1);
for (int x,y; Q--;) {
cin >> x >> y;
LL ans = 1e18;
for (int u=x; u; u=par[u]) {
if (mp[u].count(x) && mp[u].count(y)) {
cmin(ans, mp[u][x]+mp[u][y]);
}
}
for (int v=y; v; v=par[v]) {
if (mp[v].count(x) && mp[v].count(y)) {
cmin(ans, mp[v][x]+mp[v][y]);
}
}
cout << ans << "\n";
}
}
标签:int,100100,st,联训,mp,dis,first 来源: https://www.cnblogs.com/orchid-any/p/16522450.html