2018.08.30 NOIP模拟 graph(dfs序/树剖+线段树)

【描述】

给你一个图,一共有 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


今天考试唯一开了数组的题。
貌似就是讨论一下u和v的关系,
如果u是v的祖先直接求链的值,不然的话查询以u为根的子树中回到根的最优值就行了。
要维护这个信息,就dfs序/树剖把树给映射到序列上,维护dis[i]+w[i]的最优值,其中dis[i]表示i到根的距离,w[i]表示i回到根的距离。
修改第一类边显然是子树修改。
第二类边是单点修改。
还是挺简单的。
代码:

#include<bits/stdc++.h>
#define N 100005
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
#define ll long long
using namespace std;
inline ll read(){
    ll ans=0;
    char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans;
}
inline void write(ll x){
    if(x>9)write(x/10);
    putchar((x%10)^48);
}
int n,q,first[N],cnt=0,tot=0,top[N],fa[N],dep[N],num[N],pred[N],siz[N],hson[N],rec[N];
ll a[N],d[N];
struct edge{int v,next;ll w;}e[N<<2];
struct Node{int l,r;ll mn,add;}T[N<<2];
inline void add(int u,int v,ll w){e[++cnt].v=v,e[cnt].w=w,e[cnt].next=first[u],first[u]=cnt;}
inline void dfs1(int p){
    siz[p]=1;
    for(int i=first[p];i;i=e[i].next){
        int v=e[i].v;
        a[v]=a[p]+e[i].w,dep[v]=dep[p]+1,fa[v]=p,dfs1(v),siz[p]+=siz[v];
        if(siz[v]>siz[hson[p]])hson[p]=v;
    }
}
inline void dfs2(int p,int tp){
    top[p]=tp,num[p]=++tot,pred[tot]=p;
    if(!hson[p])return;
    dfs2(hson[p],tp);
    for(int i=first[p];i;i=e[i].next){
        int v=e[i].v;
        if(v!=hson[p])dfs2(v,v);
    }
}
inline ll min(ll a,ll b){return a<b?a:b;}
inline void pushup(int p){T[p].mn=min(T[lc].mn,T[rc].mn);}
inline void pushnow(int p,int v){T[p].add+=v,T[p].mn+=v;}
inline void pushdown(int p){if(T[p].add)pushnow(lc,T[p].add),pushnow(rc,T[p].add),T[p].add=0;}
inline void build(int p,int l,int r){
    T[p].l=l,T[p].r=r,T[p].add=0;
    if(l==r){T[p].mn=a[pred[l]]+d[pred[l]];return;}
    build(lc,l,mid),build(rc,mid+1,r),pushup(p);
}
inline void update(int p,int ql,int qr,ll v){
    if(ql>T[p].r||qr<T[p].l)return;
    if(ql<=T[p].l&&T[p].r<=qr)return pushnow(p,v);
    pushdown(p);
    if(qr<=mid)update(lc,ql,qr,v);
    else if(ql>mid)update(rc,ql,qr,v);
    else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
    pushup(p);
}
inline ll query(int p,int ql,int qr){
    if(ql>T[p].r||qr<T[p].l)return 1e18;
    if(ql<=T[p].l&&T[p].r<=qr)return T[p].mn;
    pushdown(p);
    if(qr<=mid)return query(lc,ql,qr);
    if(ql>mid)return query(rc,ql,qr);
    return min(query(lc,ql,mid),query(rc,mid+1,qr));
}
inline void swap(int&x,int&y){x^=y,y^=x,x^=y;}
inline int lca(int x,int y){
    while(top[x]!=top[y]){if(dep[top[x]]<dep[top[y]])swap(x,y);x=fa[top[x]];}
    return dep[x]<dep[y]?x:y;
}
int main(){
    freopen("graph.in","r",stdin);
    freopen("graph.out","w",stdout);
    n=read(),q=read();
    for(int i=1;i<n;++i){int u=read(),v=read();ll w=read();add(u,v,w);}
    for(int i=1;i<n;++i){int u=read(),v=read();ll w=read();rec[i]=u,d[u]=w;}
    dfs1(1),dfs2(1,1),build(1,1,n);
    while(q--){
        int op=read();ll x=read(),y=read();
        if(op==1){
            if(x<=n-1)update(1,num[e[x].v],num[e[x].v]+siz[e[x].v]-1,y-e[x].w),e[x].w=y;
            else update(1,num[rec[x-n+1]],num[rec[x-n+1]],y-d[rec[x-n+1]]),d[rec[x-n+1]]=y;
        }
        else{
            if(x==y){printf("0\n");continue;}
            if(lca(x,y)==x)write(query(1,num[y],num[y])-d[y]-query(1,num[x],num[x])+d[x]);
            else write(query(1,num[y],num[y])+query(1,num[x],num[x]+siz[x]-1)-query(1,num[x],num[x])+d[x]-d[y]);
            puts("");
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/9738333.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值