给定一个n(n<=1e5)个点的树,边权w(1<=w<=1e6)
给定一个长为n的01串,为1的代表是加油站,
在加油站,每加一次油的时间是T(1<=T<=1e6),每个点可以无数次加油
初始速度v是1,每加一次油,当前速度v就会乘2,而通过一条边的时间是
q(q<=1e5)次询问,每次给出u、v,询问从u到v的最短时间
实际t(t<=1e4)组样例,但sumn和sumq不超过1e5
官方题解
对于每组询问,先算一个不加油的时间,然后考虑加油的情况,
由于w是1e6,所以最多加油20次,通过一条边的时间就可以控制在1s
所以可以枚举加油次数k,加油次数固定后,考虑这个从u到v的过程
1. v=1,从u走到链上点x
2. v=1,从x走到最近加油站y,加油k次;v=,从y回到x
3. v=,从x去v
可能会考虑说,第二步中,如果x对应的y在x和v之间,是不需要从y回到x的,
不过,此时链上x处的答案,会不如y处的答案,所以还是不影响最优解的
由于边通过的时间,有向上取整,现算比较困难
所以,可以建k棵树出来,边权是加油后原来的边权除以速度向上取整的值
而1-3这三步中,2是可以预处理出来的,
可以用所有加油站跑多源dijkstra,得到每个点到最近加油站的加油前去的时间&加油后去的时间
相当于对于u->v链上的每个点x,都得到一个对应的时间,
而第一步、第三步的时间中,关于x的部分也可以拆出来维护到x的项上
此外,u到lca前半段,lca到v后半段,两段的式子会有不同,所以需要分别维护mn和mn2数组
具体来说,按照官方题解的式子,需要写作:
如果在前半段(官方题解typo)是第一个式子,否则是第二个式子,而v1就是刚才提到的x
由于空间问题,所以需要将询问离线后,
再枚举所有k处理所有答案后,再回答
复杂度大致为O((n+q)*logw*logn)
思路比较朴素,但粘了若干个板子,细节较多,比较难调,所以比较难赛中AC
int lc=lca(u,v),d1=dep[u]-dep[lc]+1,d2=dep[v]-dep[lc]+1;
这里有个点需要注意,d1和d2分别等于u到lca的距离+1,v到lca的距离+1
较实际距离加了1,是因为:
lca的倍增f[x][0]是x的父亲的值,
而维护的最小值的倍增mn[x][0]是x自己的值
相当于前者是开区间,后者是闭区间,所以用到后者时,需要加1
#include
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define sci(n) scanf("%d",&n)
#define pb push_back
#define SZ(a) (int)(a.size())
#define fi first
#define se second
typedef long long ll;
typedef pair P;
typedef pair PL;
const int N=1e5+10,M=20,L=17;
const ll INF=0x3f3f3f3f3f3f3f3fll;
vectorE[N];
int n,T,f[N][L+1],dep[N];
ll dis[M+1][N],mn[N][L+1],mn2[N][L+1],sum[M+1][N],ans[N];
P ask[N];
bool vis[N];
char s[N];
void init2(ll a[][L+1]){// 树上倍增
rep(i,1,L){
rep(j,1,n){
a[j][i]=min(a[j][i-1],a[f[j][i-1]][i-1]);
}
}
}
void dfs(int u,int fa){
for(auto &x:E[u]){
int v=x.fi,w=x.se;
if(v==fa)continue;
dep[v]=dep[u]+1;
rep(i,0,M){
ll bs=1<,greater>pq;
rep(j,0,k){
ll bs=1<dis[j][u]+w){
dis[j][v]=dis[j][u]+w;
pq.push(PL(dis[j][v],v));
}
}
}
}
}
inline int lca(int x,int y){
if(dep[x]>i&1)x=f[x][i];
if(x==y)return x;
per(i,L,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
ll amn(int x,int d,ll a[][L+1],ll s){
ll ans=INF;
per(i,L,0){
if(d>>i&1){
ans=min(ans,s+a[x][i]);
x=f[x][i];
}
}
return ans;
}
ll sol(int u,int v,int k){
if(u==v)return 0;
int lc=lca(u,v),d1=dep[u]-dep[lc]+1,d2=dep[v]-dep[lc]+1;
ll s1=sum[0][u]+sum[k][v]-2*sum[k][lc];
ll s2=sum[0][u]+sum[k][v]-2*sum[0][lc];
ll ans=min(amn(u,d1,mn,s1),amn(v,d2,mn2,s2));
return ans;
}
void read(){
sci(n);sci(T);
rep(i,1,n)E[i].clear();
int u,v,w;
rep(i,1,n-1){
sci(u),sci(v),sci(w);
E[u].push_back(P(v,w));
E[v].push_back(P(u,w));
}
scanf("%s",s+1);
dfs(1,0);
dijkstra(M);
}
void sol(){
int q,u,v;
sci(q);
rep(i,1,q){
sci(u),sci(v);
ask[i]={u,v};
ans[i]=sum[0][u]+sum[0][v]-2*sum[0][lca(u,v)];
}
rep(k,1,M){
rep(j,1,n){
mn[j][0]=dis[k][j]-sum[0][j]+sum[k][j];
mn2[j][0]=dis[k][j]+sum[0][j]-sum[k][j];
}
init2(mn);init2(mn2);
rep(i,1,q){
auto [u,v]=ask[i];
ans[i]=min(ans[i],1ll*k*T+sol(u,v,k));
}
}
rep(i,1,q){
printf("%lld\n",ans[i]);
}
}
int main(){
int t;
sci(t);
while(t--){
read();
sol();
}
return 0;
}