CodeForces 838B Diverging Directions 兼【20180808模拟测试】t3

描述

给你一个图,一共有 N 个点,2*N-2 条有向边。
边目录按两部分给出
1、 开始的 n-1 条边描述了一颗以 1 号点为根的生成树,即每个点都可以由 1 号点到达。
2、 接下来的 N-1 条边,一定是从 i 到 1(2<=i<=N)的有向边,保证每个点都能到达
有 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
第一反应,树链剖分+线段树,这么裸,欺负我昨天晚上才看的树剖板
诶等等?怎么会有边权?
然后这个只会敲板子的蒟蒻就懵逼掉了
然后今天蒟蒻的玄学贪心1分儿也没骗到
于是本蒟蒻成功爆0

关于正解

令 ai表示从 i 到根的边的长度。对于每个点维护 disti表示从根到 i 的路径, mindisti表示 i 的子树中 distj+aj的最小值。
然后对于询问 (u,v)分类:
如果 u 是 v的祖先,由于权值都是正整数,答案为distv−distu。
否则答案为mindistu−distu+distv。
dist和 mindist用 dfs序+线段树维护就可以了。

具体维护
维护子树 1->i->1 的值 dist,每个点记录第一次的 dfs 序 st[i],子树结束的 ed[i]
当边 u->v 修改为 w,则 st[v]…ed[v] 增加 w- w’,w’表示 u->v 原来的值。同时,当边 u->1 修改为 w:则 st[u]..st[u]增加 w-w’

好了,那么搬上代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 200010
using namespace std;
struct Edge{
    int u,v,w,nxt;
}e[N<<1];
long long sum[N<<2],p[N<<2],dis[N],add[N<<2];
int dep[N],top[N],son[N],l[N],r[N],s[N],u[N],w[N],x1,y1,z1;
int h[N],tot;
int k,cnt=0,n,Q,a[N],L;
void addd(int x,int y,int z){
    e[++cnt].u=x;e[cnt].v=y,e[cnt].w=z;e[cnt].nxt=h[x],h[x]=cnt,u[y]=x;
}
//以下dfs序与lca部分
void dfs1(int x,int y){
    dep[x]=++y;l[x]=++tot;s[x]=1;w[tot]=x;
    for(int i=h[x];i;i=e[i].nxt){
        dis[e[i].v]=dis[x]+e[i].w;
        dfs1(e[i].v,y);
        s[x]+=s[e[i].v];
        if(s[e[i].v]>s[son[x]])son[x]=e[i].v;
    }
    r[x]=tot;
}
void dfs2(int x,int y){
    top[x]=y;
    if(son[x])dfs2(son[x],y);
    for(int i=h[x];i;i=e[i].nxt)
    if(e[i].v!=son[x])dfs2(e[i].v,e[i].v);
}
int Lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]>dep[top[y]])
            x=u[top[x]];
        else y=u[top[y]];
        }
    return dep[x]>dep[y]?y:x;
}
//以下线段树部分(谢天谢地我终于搞出了没有结构体的线段树)
void Up(int x){
    sum[x]=min(sum[x<<1],sum[x<<1|1]);
}
void Build(int x,int l,int r){
    if(l==r){
        sum[x]=dis[w[l]]+a[w[l]];
        add[x]=dis[w[l]];
        return;
    }
    int Mid=l+r>>1;
    Build(x<<1,l,Mid);
    Build(x<<1|1,Mid+1,r);
    Up(x);
}
inline void Down(int x){
    if(p[x]){
        sum[x<<1]+=p[x];add[x<<1]+=p[x];p[x<<1]+=p[x];
        sum[x<<1|1]+=p[x];add[x<<1|1]+=p[x];p[x<<1|1]+=p[x];
        p[x]=0;
    }
}
inline void Update1(int x,int l,int r,int y,int z){
    if(l==r){
        sum[x]+=z;
        return;
    }
    Down(x);
    int Mid=l+r>>1;
    if(y<=Mid)Update1(x<<1,l,Mid,y,z);else Update1(x<<1|1,Mid+1,r,y,z);
    Up(x);
}
inline void Update2(int x,int l,int r,int L,int R,int y){
    if(l>R||r<L)return;
    if(l>=L&&r<=R){
        sum[x]+=y;add[x]+=y;p[x]+=y;
        return;
    }
    Down(x);
    int Mid=l+r>>1;
    Update2(x<<1,l,Mid,L,R,y);Update2(x<<1|1,Mid+1,r,L,R,y);
    Up(x);
}
long long Query(int x,int l,int r,int L,int R){
    if(l>R||r<L)return 1e18;
    if(l>=L&&r<=R)return sum[x];
    Down(x);
    int Mid=l+r>>1;
    return min(Query(x<<1,l,Mid,L,R),Query(x<<1|1,Mid+1,r,L,R));
}
long long Query2(int x,int l,int r,int y){
    if(l==r)return add[x];
    Down(x);
    int Mid=l+r>>1;
    if(y<=Mid)return Query2(x<<1,l,Mid,y);
    return Query2(x<<1|1,Mid+1,r,y);
}
int main(){
    scanf("%d%d",&n,&Q);
    for(int i=1;i<n;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        addd(x,y,z);
    }
    for(int i=1;i<n;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        e[++cnt].u=x;e[cnt].v=y;a[x]=z;
    }
    tot=0;
    dfs1(1,0);
    dfs2(1,1);
    Build(1,1,n);
    while(Q--){
        scanf("%d%d%d",&k,&x1,&y1);
        if(k==1){
            if(x1>=n)Update1(1,1,n,l[e[x1].u],y1-a[e[x1].u]),a[e[x1].u]=y1;
            else Update2(1,1,n,l[e[x1].v],r[e[x1].v],y1-e[x1].w),e[x1].w=y1;
        }else{
            L=Lca(x1,y1);
            if(L==x1)printf("%lld
",Query2(1,1,n,l[y1])-Query2(1,1,n,l[x1]));
            else printf("%lld
",Query(1,1,n,l[x1],r[x1])-Query2(1,1,n,l[x1])+Query2(1,1,n,l[y1]));
        }
    }
    return 0;
}

我想我的代码比起某标程已经相当好看了,只要是知道线段树与树剖的结合题解都应该看得懂,除了个别变量名在改动时被懒惰的本蒟蒻张冠李戴之外没有什么难以理解的地方了。
ps(据说有隔壁巨佬3h就调好了还嫌慢我这个6h的并不敢发言)

原文地址:https://www.cnblogs.com/lisuier/p/9535095.html