CF1859F

问题链接

现有一棵大小为105的有边权树和最多105次询问,每次询问树上两点uv需要的最短时间

与直接求路径长度不同的是,你的速度是可以变化的。你的初始速度c=1,在可以练习的地点,你可以花费时间T使得你的速度c=c×2,而你经过每条路径所需的时间为wic

易得以下三点

  • 对于一组询问,你的移动路径有可能会经过重复的点
  • 你一定只在第一个经过的可以练习的点进行练习
  • 练习次数不会超过maxi=1nwi,答案不会超过2×109

对于当前速度为c=2k,在任意两点uv之间移动所需时间,把这个值设为disu,v,k

我们首先可以倍增预处理一个点到他所有2的整数次方的父亲,然后在log2n的时间内求出disu,v,k

如果我们的路径不是从u直达v,那么一定是从某个点离开路径练车,再由这个点返回路径

出发点为u,终点为v,假设我们从点x离开路径,在点y练车,我们所需要的时间就是

mini=120(disu,x,0+disx,y,0+disy,x,i+disx,v,i+T×i)ORdisu,v,0

后者我们可以直接求出,前者我们先用BFS维护出每个点x前往一个点练车i次然后回到该点的最小时间,把这个值设为dissx,i

可以对倍增中的每个区间x到他的往上第2y个父亲,维护出

  • dis1x,y,i = 从x出发,练了0次车,最后到达父亲y,练了i次车的最小时间
  • dis2x,y,i = 从x的父亲y出发,练了0次车,最后到达x,练了i次车的最小时间

dis1x,0,i=dis2x,0,i=dissx,i

dis1x,y,i=min(dis1x,y1,i+disfx,y1,y1,i,disx,y1,0+dis1fx,y1,y1,i)

dis2x,y,i=min(dis2x,y1,i+disfx,y1,y1,0,disx,y1,i+dis1fx,y1,y1,i)

然后对每个区间都按照这个求一遍最小的距离,一定包含最优解

long long会mle,每个区间的答案对2×109min,时间复杂度nlog3n

进行前缀和优化可以达到nlog2n

#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 国际」许可协议进行许可。

posted @   Linxrain  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示