题解:P14706 [ICPC 2023 Tehran R] Cup of Tea
来一个
首先你可以考虑一个暴力建图的做法,你发现一定是从某个茶馆开始走,每次走不超过
考虑如何优化建这些边,树上距离不超过
::::success[code]
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
const int maxn = 3e5 + 10;
const int maxv = maxn * 21;
const int inf = (1ll << 61);
int n,k,rt,tot,cnt;
int a[maxn],siz[maxn],mx[maxn];
bool vis[maxn];
vector<pii> G[maxn];
struct edge{
int v,w;
};
vector<edge> H[maxv];
int dis[maxv];
pii b[maxn];
int id[maxn],num;
void add(int u,int v,int w) {H[u].push_back({v,w});}
void getroot(int u,int fa)
{
siz[u] = 1;
mx[u] = 0;
for(auto it : G[u])
{
int v = it.fi;
if(v == fa || vis[v]) continue;
getroot(v,u);
siz[u] += siz[v];
mx[u] = max(mx[u],siz[v]);
}
mx[u] = max(mx[u],tot - siz[u]);
if(mx[u] < mx[rt]) rt = u;
}
void getdis(int u,int fa,int d)
{
if(d > k) return;
b[++num] = {d,u};
for(auto it : G[u])
{
int v = it.fi,w = it.se;
if(v == fa || vis[v]) continue;
getdis(v,u,d + w);
}
}
void build(int u)
{
num = 0;
getdis(u,0,0);
sort(b + 1,b + num + 1);
for(int i = 1;i <= num;i++)
{
id[i] = ++cnt;
add(id[i],b[i].se,b[i].fi);
if(i > 1) add(id[i],id[i - 1],0);
}
int j = num;
for(int i = 1;i <= num;i++)
{
while(j && b[i].fi + b[j].fi > k) j--;
int x = b[i].se;
if(a[x] && j) add(x,id[j],b[i].fi);
}
}
void divide(int u)
{
vis[u] = 1;
build(u);
for(auto it : G[u])
{
int v = it.fi;
if(vis[v]) continue;
tot = siz[v];
mx[rt = 0] = inf;
getroot(v,u);
getroot(rt,0);
divide(rt);
}
}
void dij(int s)
{
for(int i = 1;i <= cnt;i++) dis[i] = inf;
priority_queue<pii,vector<pii>,greater<pii>> q;
dis[s] = 0; q.push({0,s});
while(!q.empty())
{
auto it = q.top();
q.pop();
int u = it.se,d = it.fi;
if(d != dis[u]) continue;
for(auto e : H[u])
{
int v = e.v,w = e.w;
if(dis[v] > d + w)
{
dis[v] = d + w;
q.push({dis[v],v});
}
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin >> n >> k;
for(int i = 1;i < n;i++)
{
int u,v,w;
cin >> u >> v >> w;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
for(int i = 1;i <= n;i++) cin >> a[i];
cnt = n;
tot = n;
mx[rt = 0] = inf;
getroot(1,0);
getroot(rt,0);
divide(rt);
dij(1);
for(int i = 2;i <= n;i++) cout << (dis[i] == inf ? -1 : dis[i]) << ' ';
cout << '\n';
return 0;
}
::::