习题:树上的最短路(树链剖分优化建图)

题目

下水道的主干路由n个节点和\(n-1\)条边所组成,每条边通过它都需要一个时间\(t_i\),这种边是双向的

下水道上有一些塌陷,我们用\((l_1,r_1,l_2,r_2,c)\)来描述,表示从\(l_1\)到\(r_1\)路径上的点,到\(l_2\)和\(r_2\)路径上的任意一个点所需要的时间为\(c\),注意塌陷是单向的

求每一个点到目标节点\(k\)的最快时间

思路

到\(k\)转换\(k\)到每一个点,塌陷反过来建就好了

之后考虑塌陷

直接树链剖分暴力建图即可

代码

#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<cstdio>
using namespace std;
void read(int &x)
{
    x=0;
    int f=1;
    char c=getchar();
    while('0'>c||c>'9')
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while('0'<=c&&c<='9')
    {
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    x*=f;
}
void read(long long &x)
{
    x=0;
    int f=1;
    char c=getchar();
    while('0'>c||c>'9')
    {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while('0'<=c&&c<='9')
    {
        x=(x<<3)+(x<<1)+c-'0';
        c=getchar();
    }
    x*=f;
}
void write(long long x)
{
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
}
struct node
{
    #define MAXN 250005
    struct node_tre
    {
        int l;
        int r;
    }tre[2][MAXN*4];
    struct node_g
    {
        int e;
        long long w;
        friend bool operator < (const node_g &a,const node_g &b)
        {
            return a.w>b.w;
        }
    };
    int n,m,s;
    int cnt;
    vector<node_g> tree[MAXN];
    vector<node_g> g[9*MAXN+100005*2];
    int id[MAXN];
    int wson[MAXN];
    int siz[MAXN];
    int ori[MAXN];
    int dep[MAXN];
    int top[MAXN];
    int fa[MAXN];
    long long dis[9*MAXN+100005*2];
    bool vis[9*MAXN+100005*2];
    priority_queue<node_g> q;
    #undef MAXN
    void add_edge(int u,int v,long long w)
    {
        tree[u].push_back((node_g){v,w});
    }
    void dfs1(int u,int fa)
    {
        int maxx=-1;
        siz[u]=1;
        dep[u]=dep[fa]+1;
        for(int i=0;i<tree[u].size();i++)
        {
            int v=tree[u][i].e;
            if(v!=fa)
            {
                dfs1(v,u);
                siz[u]+=siz[v];
                if(siz[v]>maxx)
                {
                    maxx=siz[v];
                    wson[u]=v;
                }
            }
        }
    }
    void dfs2(int u,int top_chain)
    {
        top[u]=top_chain;
        id[u]=++cnt;
        ori[cnt]=u;
        if(wson[u])
            dfs2(wson[u],top_chain);
        for(int i=0;i<tree[u].size();i++)
        {
            int v=tree[u][i].e;

            if(v!=wson[u]&&dep[v]==dep[u]+1)
            {
                fa[v]=u;
                dfs2(v,v);
            }

        }
    }
    void build(int l,int r,int k)
    {
        tre[0][k].l=tre[1][k].l=l;
        tre[0][k].r=tre[1][k].r=r;
        if(l==r)
        {
            g[l].push_back((node_g){n+k,0});
            g[5*n+k].push_back((node_g){l,0});
            return;
        }
        g[n+(k<<1)].push_back((node_g){n+k,0});
        g[n+(k<<1|1)].push_back((node_g){n+k,0});
        g[5*n+k].push_back((node_g){5*n+(k<<1),0});
        g[5*n+k].push_back((node_g){5*n+(k<<1|1),0});
        int mid=(l+r)>>1;
        build(l,mid,k<<1);
        build(mid+1,r,k<<1|1);
    }
    void init()
    {
        dfs1(1,0);
        dfs2(1,1);
        build(1,n,1);
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<tree[i].size();j++)
            {
                int v=tree[i][j].e;
                long long w=tree[i][j].w;
                g[id[i]].push_back((node_g){id[v],w});
                g[id[v]].push_back((node_g){id[i],w});
            }
        }
        cnt=9*n;
    }
    void change_tre(int l,int r,int id,int k,int opt)
    {
        if(l>tre[opt][k].r||tre[opt][k].l>r)
            return;
        if(l<=tre[opt][k].l&&tre[opt][k].r<=r)
        {
            if(opt==0)
                g[n+k].push_back((node_g){id,0});
            else
                g[id].push_back((node_g){5*n+k,0});
            return;
        }
        change_tre(l,r,id,k<<1,opt);
        change_tre(l,r,id,k<<1|1,opt);
    }
    void change(int x,int y,int st,int opt)
    {
        while(top[x]!=top[y])
        {
            if(dep[top[x]]>dep[top[y]])
                swap(x,y);
            change_tre(id[top[y]],id[y],st,1,opt);
            y=fa[top[y]];
        }
        if(dep[x]<dep[y])
            swap(x,y);
        change_tre(id[y],id[x],st,1,opt);
    }
    void link(int l1,int r1,int l2,int r2,long long w)
    {
        change(l1,r1,++cnt,0);
        change(l2,r2,++cnt,1);
        g[cnt-1].push_back((node_g){cnt,w});
    }
    void dij()
    {
        memset(dis,0x3f,sizeof(dis));
        q.push((node_g){id[s],0});
        dis[id[s]]=0;
        while(!q.empty())
        {
            node_g u=q.top();
            q.pop();
            if(vis[u.e])
                continue;
            vis[u.e]=1;
            for(int i=0;i<g[u.e].size();i++)
            {
                int v=g[u.e][i].e;
                long long w=g[u.e][i].w;
                if(dis[u.e]+w<dis[v])
                {
                    dis[v]=w+dis[u.e];
                    q.push((node_g){v,dis[v]});
                }
            }
        }
    }
    void pr()
    {
        for(int i=1;i<=n;i++)
        {
            write(dis[id[i]]);
            putchar('\n');
        }
    }
}tre;
int main()
{
    read(tre.n);
    read(tre.m);
    read(tre.s);
    for(int i=1,u,v;i<tre.n;i++)
    {
        long long w;
        read(u);
        read(v);
        read(w);
        tre.add_edge(u,v,w);
        tre.add_edge(v,u,w);
    }
    tre.init();
    for(int i=1,l1,r1,l2,r2;i<=tre.m;i++)
    {
        long long w;
        read(l1);
        read(r1);
        read(l2);
        read(r2);
        read(w);
        swap(l1,l2);
        swap(r1,r2);
        tre.link(l1,r1,l2,r2,w);
    }
    tre.dij();
    tre.pr();
    return 0;
}

原文地址:https://www.cnblogs.com/loney-s/p/12237607.html

时间: 2024-10-13 16:20:23

习题:树上的最短路(树链剖分优化建图)的相关文章

【bzoj4034】【HAOI2015】【树上操作】【树链剖分】

Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. Input 第一行包含两个整数 N, M .表示点数和操作数. 接下来一行 N 个整数,表示树中节点的初始权值. 接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) . 再

4.15 省选模拟赛 编码 trie树 前缀和优化建图 2-sat

好题 np. 对于20分 显然可以爆搜. 对于50分 可以发现每个字符串上的问号要么是0,要么是1.考虑枚举一个字符串当前是0还是1 这会和其他字符串产生矛盾. 所以容易 发现这是一个2-sat问题. 拆点 把任意两个产生矛盾的字符串进行连边.然后最后判矛盾即可. n^2枚举 建图 判断矛盾时使用字符串hash 要分类讨论4种情况. using namespace std; const int MAXN=1010,maxn=500010,cc1=19260817,cc2=114514; int

bzoj4811 [Ynoi2017]由乃的OJ 树链剖分+贪心+二进制

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=4811 题解 我现在为什么都写一题,调一天啊,马上真的退役不花一分钱了. 考虑这道题的弱化版 NOI2014 起床困难综合证的做法. 分成每一位来考虑,如果高位可以是 \(1\) 的话,那么尽量让高位为 \(1\). 求出当前位为 \(0/1\) 时,最终得到的是 \(0\) 还是 \(1\).因为要保证选的数小于 \(z\),所以对于都可以得到 \(1\) 的情况,尽量选择 \(0\) 可以

bzoj3694: 最短路(树链剖分/并查集)

bzoj1576的帮我们跑好最短路版本23333(双倍经验!嘿嘿嘿 这题可以用树链剖分或并查集写.树链剖分非常显然,并查集的写法比较妙,涨了个姿势,原来并查集的路径压缩还能这么用... 首先对于不在最短路径树上的边x->y,设t为最短路径树上lca(x,y),则t到y上的路径上的点i到根的距离都可以用h[x]+dis[x][y]+h[y]-h[i](h[]为深度)来更新,因为h[i]一定,只要让h[x]+dis[x][y]+h[y]最小就行,这里用树剖直接修改整条链上的数,就可以过了. 并查集的

bzoj 4034: [HAOI2015]树上操作 树链剖分+线段树

4034: [HAOI2015]树上操作 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4352  Solved: 1387[Submit][Status][Discuss] Description 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有

洛谷 P2590 树的统计 P3178 树上操作【树链剖分入门】

题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 输入格式: 输入文件的第一行为一个整数n,表示节点的个数. 接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连

UOJ#53. 【UR #4】追击圣诞老人 树链剖分 k短路

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ53.html 题意 给定一棵有 n 个节点的树. 每一个点有一个权值. 对于每一个 $i$ 给定三个参数 $a_i,b_i,c_i$ ,从第 $i$ 个点出发下一步能到达的点 x 需要满足以下三个要求之一: 1. x 在 $a_i$ 到 $b_i$ 的简单路径上. 2. x 在 $a_i$ 到 $c_i$ 的简单路径上. 3. x 在 $c_i$ 到 $b_i$ 的简单路径上. 问从任意一个点出发,经过

[HAOI2015]树上操作(树链剖分)

[HAOI2015]树上操作(luogu) Description 题目描述 有一棵点数为 N 的树,以点 1 为根,且树点有边权.然后有 M 个操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 输入格式 第一行包含两个整数 N, M .表示点数和操作数.接下来一行 N 个整数,表示树中节点的初始权值.接下来 N-1 行每行两个正整数 from, to

bzoj4034 树上操作 树链剖分+线段树

题目传送门 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权.然后有 M 个操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a . 操作 3 :询问某个节点 x 到根的路径中所有点的点权和. 思路: 由于是在刷dfs序专题的时候碰到这题,所以思路被限制了,没想树链剖分的东西,没能做出来,后来发现了一个 大佬的博客,发现也是可以做的,但是这个做法看不懂...留坑 现在用树链剖分的方法,每个点的权值就是点本身