CF1859F
现有一棵大小为
与直接求路径长度不同的是,你的速度是可以变化的。你的初始速度
易得以下三点
- 对于一组询问,你的移动路径有可能会经过重复的点
- 你一定只在第一个经过的可以练习的点进行练习
- 练习次数不会超过
,答案不会超过
对于当前速度为
我们首先可以倍增预处理一个点到他所有
如果我们的路径不是从
出发点为
后者我们可以直接求出,前者我们先用
可以对倍增中的每个区间
= 从 出发,练了 次车,最后到达父亲 ,练了 次车的最小时间 = 从 的父亲 出发,练了 次车,最后到达 ,练了 次车的最小时间
然后对每个区间都按照这个求一遍最小的距离,一定包含最优解
开
进行前缀和优化可以达到
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
#define pli pair<ll,int>
#define bi (1<<i)
using namespace std;
int dis[100005][18][21],diss[100005][21];
int dis1[100005][18][21],dis2[100005][18][21];
int fa[100005][18],dep[100005];
bool vis[100005],vs[100005];
vector<pii>e[100005];
int n;
ll t;
int add(int x,int y){
return min((ll)2e9,(ll)x+y);
}
void dfs(int x,int f){
vis[x]=1;
fa[x][0]=f;
dep[x]=dep[f]+1;
for(int i=1;i<=20;i++){
dis[x][0][i]=dis[x][0][0]>>i;
if(dis[x][0][0]%bi)dis[x][0][i]++;
}
for(int i=1;i<=17;i++){
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int j=0;j<=20;j++)
dis[x][i][j]=add(dis[fa[x][i-1]][i-1][j],dis[x][i-1][j]);
}
for(int i=0;i<=20;i++){
dis1[x][0][i]=add(diss[x][i],dis[x][0][i]);
dis2[x][0][i]=add(diss[f][i],dis[x][0][i]);
}
for(int i=1;i<=17;i++)
for(int j=0;j<=20;j++){
int f=fa[x][i-1];
dis1[x][i][j]=min(add(dis1[x][i-1][j],dis[f][i-1][j]),add(dis[x][i-1][0],dis1[f][i-1][j]));
dis2[x][i][j]=min(add(dis2[x][i-1][j],dis[f][i-1][0]),add(dis[x][i-1][j],dis2[f][i-1][j]));
}
for(auto u:e[x])
if(!vis[u.first]){
dis[u.first][0][0]=u.second;
dfs(u.first,x);
}
}
int lca(int u,int v){
if(dep[u]<dep[v])swap(u,v);
for(int i=17;i>=0;i--)
if(dep[u]-dep[v]>=bi)u=fa[u][i];
if(u==v)return u;
for(int i=17;i>=0;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
ll sum(int u,int v,int m){
ll cnt=0;
int l=lca(u,v);
for(int i=17;i>=0;i--){
if(dep[u]-dep[l]>=bi)
cnt+=dis[u][i][m],u=fa[u][i];
if(dep[v]-dep[l]>=bi)
cnt+=dis[v][i][m],v=fa[v][i];
}
return cnt;
}
ll qry(int u,int v){
int l=lca(u,v);
ll cnt[21],ans=sum(u,v,0);
for(int i=0;i<=20;i++)cnt[i]=0;
for(int i=17;i>=0;i--)
if(dep[u]-dep[l]>=bi){
int f=fa[u][i];
for(int j=1;j<=20;j++)
ans=min(ans,dis1[u][i][j]+sum(f,v,j)+cnt[0]+t*j);
cnt[0]+=dis[u][i][0];
u=fa[u][i];
}
for(int i=17;i>=0;i--)
if(dep[v]-dep[l]>=bi){
int f=fa[v][i];
for(int j=1;j<=20;j++){
ans=min(ans,dis2[v][i][j]+sum(f,u,0)+cnt[0]+cnt[j]+t*j);
cnt[j]+=dis[v][i][j];
}
v=fa[v][i];
}
return ans;
}
void bfs(int x){
priority_queue<pli,vector<pli>,greater<pli>>q;
for(int i=1;i<=n;i++){
diss[i][x]=1e18;
if(vs[i]){
q.push({0,i});
diss[i][x]=0;
}
}
while(!q.empty()){
int now=q.top().second;
ll d=q.top().first;
q.pop();
if(d>diss[now][x])continue;
for(auto u:e[now])
if(diss[u.first][x]>d+1+(u.second-1>>x)+u.second){
diss[u.first][x]=d+1+(u.second-1>>x)+u.second;
if(diss[u.first][x]>=2e9)continue;
q.push({diss[u.first][x],u.first});
}
}
}
void solve(){
scanf("%d%lld",&n,&t);
for(int i=1;i<=n;i++){
vis[i]=0;
e[i].clear();
}
for(int i=1;i<n;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e[u].push_back({v,w});
e[v].push_back({u,w});
}
for(int i=1;i<=n;i++){
int x;
scanf("%1d",&x);
vs[i]=x;
}
for(int i=0;i<=20;i++)
bfs(i);
dfs(1,0);
int q;
scanf("%d",&q);
for(int i=1;i<=q;i++){
int u,v;
scanf("%d%d",&u,&v);
printf("%lld\n",qry(u,v));
}
}
int main(){
int t;
scanf("%d",&t);
while(t--)solve();
return 0;
}
作者:Linxrain
出处:https://www.cnblogs.com/Linxrain/p/17646019.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】