【2018/08/30测试T3】【SDOJ 3740】graph

【题目】

题目描述:

给你一个图,一共有 N 个点,2 * N - 2 条有向边。

边目录按两部分给出

(1)开始的 N-1 条边描述了一颗以 1 号点为根的生成树,即每个点都可以由 1 号点到达。
(2)接下来的 N-1 条边,一定是从 i 到 1(2 ≤ i ≤ N)的有向边,保证每个点都能到 1。

有 q 次询问:

1 x w :表示将第 x 条边的边权修改为 w
2 u v :询问 u 到 v 的最短距离

输入格式:

第一行是 2 个整数 N , Q,表示一共 N 个点 Q 次询问

接下来是 N - 1 行,每行 3 个整数 U , V , W,表示了前 N - 1 条边,U 到 V 的有向边

接下来 N - 1 行,每行 3 个整数 U , V , W,描述了每个点到 1 号点的边,V == 1

接下来是 Q 行,表示 Q 次修改与询问

输出格式:

若干行,每行回答一次询问

样例数据:

输入

5 9
1 3 1
3 2 2
1 4 3
3 5 4
5 1 5
3 1 6
2 1 7
4 1 8
2 1 1
2 1 3
2 3 5
2 5 2
1 1 100
2 1 3
1 8 30
2 4 2
2 2 4

输出

0
1
4
8
100
132
10

备注:

【数据规模】
20%数据,没有修改
30%数据,2 ≤ N , Q ≤ 1000 (其中有 10% 数据没有修改)
100%数据,2 ≤ N , Q ≤ 100 000, 1 ≤ 边权 ≤ 1000,000

 

【分析】

先恭喜一下 ldx 大佬 AK 了今天的题%%%

对于蒟蒻我而言,我就不要想着 A 题了,在考后能调出来都不错了

那现在来讲题吧

我们用 dist(x) 表示从 1 经过树枝边到达 x 的距离,w(x) 表示从 x 到 1 的距离

首先来说询问,对于一组询问 u,v,有以下两种情况:

(1)u 是 v 的祖先,那么 u 到 v 的最短距离就是 dist(v) - dist(u)
(2)如果 u 不是 v 的祖先,那么 u 就只能通过一条路径到达 1,然后又从 1 到达 v。从 1 到 v 的距离唯一,就是 dist(v),但是从 u 到 1 就可能有多条,可以直接从 u 回到 1,也可以走到 u 子树中的一个点,从那个点回到 1,我们就要在这些路径中找出最短的那一条

我们考虑用 dfs 序来构建一个序列,这样 u 的子树一定会是连续的一段,我们用 st(x) 表示 x 的子树开始的位置(其实就是 x),用 en(x) 表示 x 的子树结束的位置,这些可以一遍 dfs 搞定

对于情况 2,我们要快速求出 st(u)~en(u) 之间 dist(x) + w(x) 的最小值,这个就可以用线段树维护了

对于修改的话,如果修改的边是树枝边,那么 v 以及 v 的子树的 dist 值都要发生改变;如果不是的话,那么只需要修改 u 的 w 值,这些也可以用线段树来做

然后代码中判断是否为祖先我用的是倍增法

时间复杂度O(n\cdot log\, n

 

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define M 1000005
#define inf 1ll<<60ll
using namespace std;
int n,q,t,dfn;
int first[N],v[M],w[M],next[M];
int a[N<<1],b[N<<1],c[N<<1],dep[N],num[N],start[N],end[N],father[N][25];
long long d[N],add[N<<2],minn[N<<2];
void addedge(int x,int y,int k)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=k;
}
void getdfn(int x,long long ww)
{
	int i,j;
	start[x]=++dfn;
	d[start[x]]=ww+num[x];
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		father[j][0]=x;
		dep[j]=dep[x]+1;
		getdfn(v[i],ww+w[i]);
	}
	end[x]=dfn;
}
int lca(int x,int y)
{
	int i,j;
	if(dep[x]<dep[y])  swap(x,y);
	for(i=17;i>=0;--i)
	  if(dep[father[x][i]]>=dep[y])
	    x=father[x][i];
	if(x==y)  return x;
	for(i=17;i>=0;--i)
	{
		if(father[x][i]!=father[y][i])
		{
			x=father[x][i];
			y=father[y][i];
		}
	}
	return father[x][0];
}
void build(int root,int l,int r)
{
	if(l==r)
	{
		minn[root]=d[l];
		return;
	}
	int mid=(l+r)>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
void pushdown(int root,int l,int r)
{
	add[root<<1]+=add[root];
	add[root<<1|1]+=add[root];
	minn[root<<1]+=add[root];
	minn[root<<1|1]+=add[root];
	add[root]=0;
}
void modify(int root,int l,int r,int x,int y,int k)
{
	if(l>=x&&r<=y)
	{
		add[root]+=k;
		minn[root]+=k;
		return;
	}
	int mid=(l+r)>>1;
	if(add[root])  pushdown(root,l,r);
	if(x<=mid)  modify(root<<1,l,mid,x,y,k);
	if(y>mid)  modify(root<<1|1,mid+1,r,x,y,k);
	minn[root]=min(minn[root<<1],minn[root<<1|1]);
}
long long query(int root,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	  return minn[root];
	int mid=(l+r)>>1;
	long long ans=inf;
	if(add[root])  pushdown(root,l,r);
	if(x<=mid)  ans=min(ans,query(root<<1,l,mid,x,y));
	if(y>mid)  ans=min(ans,query(root<<1|1,mid+1,r,x,y));
	return ans;
}
int main()
{
//	freopen("graph.in","r",stdin);
//	freopen("graph.out","w",stdout);
	int x,y,i,j,s;
	long long disx,disy;
	scanf("%d%d",&n,&q);
	for(i=1;i<=2*n-2;++i)
	{
		scanf("%d%d%d",&a[i],&b[i],&c[i]);
		if(i<n)  addedge(a[i],b[i],c[i]);
		else  num[a[i]]=c[i];
	}
	getdfn(1,0);
	build(1,1,n);
	for(j=1;j<=20;++j)
	  for(i=1;i<=n;++i)
	    father[i][j]=father[father[i][j-1]][j-1];
	for(i=1;i<=q;++i)
	{
		scanf("%d%d%d",&s,&x,&y);
		if(s==1)
		{
			if(x<n)  modify(1,1,n,start[b[x]],end[b[x]],y-c[x]),c[x]=y;
			else  modify(1,1,n,start[a[x]],start[a[x]],y-num[a[x]]),num[a[x]]=y;
		}
		else
		{
			if(lca(x,y)==x)
			{
				disx=query(1,1,n,start[x],start[x])-num[x];
				disy=query(1,1,n,start[y],start[y])-num[y];
				printf("%lld\n",disy-disx);
			}
			else
			{
				disx=query(1,1,n,start[x],end[x])-query(1,1,n,start[x],start[x])+num[x];
				disy=query(1,1,n,start[y],start[y])-num[y];
				printf("%lld\n",disx+disy);
			}
		}
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/82228468