瓜瓜的时空旅行,第三次模拟赛,dfs序+线段树维护最小值

题目描述

       西瓜们生活在编号 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

输入格式:




8
100
132
10

第一行 3 个整数 n,q1,q2 分别表示 平行时空,西瓜 和 时光机变化 的个数。
接下来 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


输出样例




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;
			}
		}
	}
}

      

猜你喜欢

转载自blog.csdn.net/deep_kevin/article/details/80059005