题目链接:https://www.luogu.com.cn/problem/CF1140G
给出一个n个点的树T,然后复制一份T′,每个T中的点i向T′中的点i都有连边构成一张图。
图上所有权值各不相同,现在q次询问图上两点的最短路。
1≤n≤3×105,1≤q≤6×105
因为树上两点简单路径唯一,所以x到y之间的最短路肯定是包括两棵树中x∼y路径上的某些点的。
现在问题就是从x到x′的切换问题,我们直接用最短路求出每个x到x′的最短距离,因为这样的距离肯定是在树上找一条点y,x走到点y再从y′走回x′。
然后设fi,j,p,q表示从点i出发往上跳了2j步且原来在第p棵树上,现在在第k棵树上的方案。发现这个转移可以用矩阵乘法来优化。
然后求答案的时候找LCA的时候合并f矩阵就好了。
时间复杂度:O((n+q)logn)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const ll N=3e5+10,T=21,S=2;
struct edge{
ll to,next,u,v;
}a[N<<1];
struct node{
ll a[S][S];
}c,one,G[N][T];
ll n,Q,tot,ls[N],f[N],dep[N],g[N][T];
bool v[N];
priority_queue<pair<ll,ll> > q;
node operator*(const node &a,const node &b){
memset(c.a,0x3f,sizeof(c.a));
for(ll i=0;i<S;i++)
for(ll j=0;j<S;j++)
for(ll k=0;k<S;k++)
c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]);
return c;
}
void addl(ll x,ll y,ll u,ll v){
a[++tot].to=y;
a[tot].next=ls[x];ls[x]=tot;
a[tot].u=u;a[tot].v=v;
return;
}
void dij(){
for(ll i=1;i<=n;i++)q.push(mp(-f[i],i));
while(!q.empty()){
ll x=q.top().second;q.pop();
if(v[x])continue;v[x]=1;
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;
if(f[x]+a[i].u+a[i].v<f[y]){
f[y]=f[x]+a[i].u+a[i].v;
q.push(mp(-f[y],y));
}
}
}
return;
}
void dfs(ll x,ll fa){
dep[x]=dep[fa]+1;
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;
if(y==fa)continue;
dfs(y,x);g[y][0]=x;
G[y][0].a[0][0]=min(a[i].u,f[x]+f[y]+a[i].v);
G[y][0].a[1][1]=min(a[i].v,f[x]+f[y]+a[i].u);
G[y][0].a[0][1]=min(a[i].u+f[x],a[i].v+f[y]);
G[y][0].a[1][0]=min(a[i].v+f[x],a[i].u+f[y]);
}
return;
}
node Ask(ll x,ll y){
node X=one,Y=one;bool flag=0;
if(x==y){X.a[0][0]=X.a[1][1]=0;X.a[0][1]=X.a[1][0]=f[x];return X;}
if(dep[x]<dep[y])swap(x,y),flag=1;
for(ll i=T-1;i>=0;i--)
if(dep[g[x][i]]>=dep[y])
X=X*G[x][i],x=g[x][i];
if(x==y){
if(flag)swap(X.a[0][1],X.a[1][0]);
return X;
}
for(ll i=T-1;i>=0;i--)
if(g[x][i]!=g[y][i])
X=X*G[x][i],x=g[x][i],
Y=Y*G[y][i],y=g[y][i];
X=X*G[x][0];Y=Y*G[y][0];
swap(Y.a[0][1],Y.a[1][0]);
X=X*Y;if(flag)swap(X.a[0][1],X.a[1][0]);
return X;
}
signed main()
{
memset(one.a,0x3f,sizeof(one.a));
one.a[0][0]=one.a[1][1]=0;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&f[i]);
for(ll i=1,x,y,u,v;i<n;i++){
scanf("%lld%lld%lld%lld",&x,&y,&u,&v);
addl(x,y,u,v);addl(y,x,u,v);
}
dij();dfs(1,0);
for(ll j=1;j<T;j++)
for(ll i=1;i<=n;i++){
g[i][j]=g[g[i][j-1]][j-1];
G[i][j]=G[i][j-1]*G[g[i][j-1]][j-1];
}
scanf("%lld",&Q);
while(Q--){
ll x,y;
scanf("%lld%lld",&x,&y);
node tmp=Ask((x+1)/2,(y+1)/2);
printf("%lld\n",tmp.a[!(x&1)][!(y&1)]);
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2021-07-12 CF786C-Till I Collapse【树状数组倍增,优先队列】
2021-07-12 CF802O-April Fools‘ Problem(hard)【wqs二分,优先队列】
2021-07-12 P4428-[BJOI2018]二进制【树状数组,set】
2021-07-12 P5180-[模板]支配树