题目描述
西瓜们生活在编号 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; } } } }