题目描述
西瓜们生活在编号 1⋯n 的 n个平行时空中,2n−2 台时光机将这些平行时空联系在一起。一台时光机有 3个整数参数 u,v,t 表示从时空 u 可以花费 t 的时间穿梭到时空 v。为了确保时空之间可以相互穿梭,同时方便作为现世的 1号时空的通行,西瓜们将这些时光机进行分工:前 n−1 台.时光机确保从 1号时空可以直接 / 间接抵达任意时空,后 n−1台时光机负责从 2⋯n号时空直接返回 1号时空。q1个西瓜希望从一些时空穿梭到一些时空。显然,智慧的西瓜们会选择最省时的路线。然而,时光机并不稳定,在此期间,时光机的运行时间会发生q2次变化。西瓜王被吃了,希望你告诉他的子民西瓜们当前最优路线所需的时间。
输入输出格式5 7 2
1 3 1
3 2 2
1 4 3
3 5 4
5 1 5
3 1 6
2 1 7
4 1 8
1 1 1
1 1 3
1 3 5
1 5 2
2 1 100
1 1 3
2 8 30
1 4 2
1 2 4
输入格式:
0
1
4
8
100
132
10
接下来 2n-2 行,每行3个整数 u,v,t 表示一台时光机。
接下来 q1+q2 行,每行 t,x,y 个整数 :
若 t=1,表示当前有一个西瓜想要从时空 穿梭到 时空 ;
若 t=2,表示第 台时光机的运行时间变成了 。
输出格式:
q1行,对于每个 x,y ,输出一行表示当前最优路线所需的时间。
输入样例:
5 7 2
1 3 1
3 2 2
1 4 3
3 5 4
5 1 5
3 1 6
2 1 7
4 1 8
1 1 1
1 1 3
1 3 5
1 5 2
2 1 100
1 1 3
2 8 30
1 4 2
1 2 4
0
1
4
8
100
132
10
简化题意,我们可以知道,前 n-1 条边,使 n 个点变为一棵外向树,也就是说只能从父亲节点到子树。
后 n-1 条边,表示每个节点到根节点都有1条路。
那么x到达y的方法有几种呢?
1. y在x的子树内,直接向下搜即可
2. y不在x的子树内,从x直接去到根,再从根去到y节点。
3. y不在x的子树内,先往子树走,再从那个点去根,从根去y节点(可能会比2更优)。
1就可以直接处理,直接用根到y的距离减去根到x的距离即可。
2,3看似挺简单的,做起来时间复杂度却十分的高。
其实可以把问题转换为 求子树内最小值 的问题。
那么要使得子树内区间连续,我们就可以用dfs遍历,进栈一次标记一下,出栈一次标记一下。
我们求得了一个dfs序,自然维护最小值就理所当然地用线段树了。
我们用dis数组来表示从根到x的路径长度,用to数组来表示从x到根的距离。
那么我们可以知道,设x为起点,y为终点且不在x的子树内,i为x的子树任一点。
就要使得dis[i]-dis[x]+to[i]+dis[y]最小。
变换一下公式可得:dis[i]+to[i]-dis[x]+dis[y].
很容易可以发现,-dis[x]+dis[y]是不变的,所以要使原式最小,我们就要是的dis[i]+to[i]最小。
所以线段树维护的就是这个dis[i]+to[i].
在打代码的时候发现dis[i]数组不能及时的更新,所以我们可以不用知道dis,直接用线段树中的值减去to[i]来表示即可。
改边:
1.改的是前n-1条边,要更改v节点及其子树到根的距离(加上 新的值-(dis[v]-dis[u])(原来边权))(dis求法如上)
2.改的是后n-1条边,直接改to[u],更新线段树的u值(加上 新的值-to[u])
最后要注意在dfs遍历时要把当前子树的编号的最小值(l[i])和最大值(r[i])记录下来。
很明显当前点的编号就是l[i].这个编号(为了线段树维护)和原编号(平行时空)不要搞混。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int n,m,q;
struct edge{
int x,y,next;
long long c;
}s[200010];
struct tree{
int ls,rs;
int x,y;
long long lazy;
long long mmin;
}tr[400010];
long long to[200010];
long long dis[200010];
int len=0;
int first[200010];
int z=0,l[200010],r[200010];
int op[200010];
void ins(int x,int y,long long c){
len++;
s[len].x=x;s[len].y=y;s[len].c=c;s[len].next=first[x];first[x]=len;
}
void bt(int x,int y){
len++;
tr[len].x=x;tr[len].y=y;
tr[len].ls=-1;tr[len].rs=-1;
tr[len].mmin=1e16;
tr[len].lazy=0;
if(x==y) {
tr[len].mmin=0;
return ;
}
int mid=(x+y)/2;
int i=len;
tr[i].ls=len+1;bt(x,mid);
tr[i].rs=len+1;bt(mid+1,y);
}
void pushdown(int x){
int ls=tr[x].ls,rs=tr[x].rs;
if(tr[ls].ls==-1 && tr[ls].rs==-1) tr[ls].mmin+=tr[x].lazy;
else tr[ls].lazy+=tr[x].lazy,tr[ls].mmin+=tr[x].lazy;
if(tr[rs].ls==-1 && tr[rs].rs==-1) tr[rs].mmin+=tr[x].lazy;
else tr[rs].lazy+=tr[x].lazy,tr[rs].mmin+=tr[x].lazy;
tr[x].lazy=0;
}
void change(int p,int x,int y,long long t){
if(tr[p].ls==-1 && tr[p].rs==-1) {
tr[p].mmin+=t;
return ;
}
if(tr[p].x==x && tr[p].y==y){
tr[p].lazy+=t;
tr[p].mmin+=t;
return ;
}
pushdown(p);
int mid=tr[tr[p].ls].y;
if(y<=mid) change(tr[p].ls,x,y,t);
else if(x>mid) change(tr[p].rs,x,y,t);
else change(tr[p].ls,x,mid,t),change(tr[p].rs,mid+1,y,t);
tr[p].mmin=min(tr[tr[p].ls].mmin,tr[tr[p].rs].mmin);
}
long long get_min(int p,int x,int y){
if(tr[p].x==x && tr[p].y==y)
return tr[p].mmin;
pushdown(p);
int mid=tr[tr[p].ls].y;
if(y<=mid) return get_min(tr[p].ls,x,y);
else if(x>mid) return get_min(tr[p].rs,x,y);
else return min(get_min(tr[p].ls,x,mid),get_min(tr[p].rs,mid+1,y));
}
void dfs(int x){
l[x]=++z;
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
dis[y]=dis[x]+s[i].c;
dfs(y);
}
r[x]=z;
}
int main(){
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
scanf("%d %d %d",&n,&m,&q);
for(int i=1;i<=n-1;i++){
int x,y;
long long c;
scanf("%d %d %lld",&x,&y,&c);
ins(x,y,c);
}
for(int i=1;i<=n-1;i++){
int x,y;
long long c;
scanf("%d %d %lld",&x,&y,&c);
to[x]=c;
op[i]=x;
}
dfs(1);
len=0;
bt(1,n);
for(int i=1;i<=n;i++) change(1,l[i],l[i],dis[i]+to[i]);
for(int i=1;i<=m+q;i++){
int t,x,y;
scanf("%d %d %d",&t,&x,&y);
if(t==1){
if(l[x]<=l[y] && l[y]<=r[x]) printf("%lld\n",(get_min(1,l[y],l[y])-to[y])-(get_min(1,l[x],l[x])-to[x]));
else printf("%lld\n",get_min(1,l[x],r[x])-(get_min(1,l[x],l[x])-to[x])+(get_min(1,l[y],l[y])-to[y]));
}
else {
if(x<=n-1)
change(1,l[s[x].y],r[s[x].y],y-((get_min(1,l[s[x].y],l[s[x].y])-to[s[x].y])-(get_min(1,l[s[x].x],l[s[x].x])-to[s[x].x])));
else{
x-=n-1;
change(1,l[op[x]],l[op[x]],y-to[op[x]]);
to[op[x]]=y;
}
}
}
}