【题目】
题目描述:
给你一个图,一共有 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()
【代码】
#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;
}