luogu P4949 最短距离

Posted cold-cold

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P4949 最短距离相关的知识,希望对你有一定的参考价值。

题目描述

给出一个 N 个点 N 条边的无向连通图。

你需要支持两种操作:

  1. 修改 第 x 条边的长度为 y ;

  2. 查询 点 x 到点 y 的最短距离。

共有 {M}M 次操作。

输入输出格式

输入格式:

输入共 N + M + 1 行:

第 1 行,包含 2 个正整数 N,M,表示点数即边数,操作次数。

第 2 行到第 N + 1 行,每行包含 3 个正整数 x,y,z,表示 x 与 y 间有一条长度 为 z 的边。

第 N + 2 到 N + M + 1 行,每行包含 3 个正整数 opt,x,y,表示操作种类,操作的参数(含义见【题目描述】)。

输出格式:

对于每次操作 2 输出查询的结果。

 

此题是一道基环树上树链剖分题目

思路是这样的:基环树其实可以看做一棵树多连了一条边,于是我们可以通过加边时维护一个并查集,来找出多的那条边。

将那条多出的边记录下来。

然后我们考虑u到v的最大值,有两种情况

  u直接在树上到v

  u,v到多出的那条边的两端,通过这条边连在一起

与是我们就可以很愉快的在树上跑树链剖分了,注意此题是边权而不是点权

那我们对于u到v权值为w的一条边可以看做u->xx->v,u,v点权为0,xx点权为w,就可将边权转化为点权了,点数翻倍,记得数组大小翻倍哟

值得一提的是我一开始线段树的数组忘记开4倍了,结果又WA又TLE40分,搞得我还以为哪里写错了呢

实现如下:

#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#define lson l,mid,o<<1
#define rson mid+1,r,o<<1|1
using namespace std;
typedef long long ll;
inline int read()
{
    int a=0,p=0;char ch=getchar();
    while((ch<0||ch>9)&&ch!=-) ch=getchar();
    if(ch==-) p=1,ch=getchar();
    while(ch>=0&&ch<=9) a=(a<<3)+(a<<1)+ch-0,ch=getchar();
    return p?-a:a;
}
const int N=200100;
int n,q,op,u,v,w,cnt,sum[N<<2],head[N],top[N],fe[N],fa[N],id[N],dep[N],size[N],son[N],ww[N],wn[N],tim=0,t1,t2,keu,kev,kew,kex,xu;
int getf(int u){return fe[u]==u?u:fe[u]=getf(fe[u]);}
struct EDGE{int nxt,to;}e[N<<1];
void add(int u,int v){e[++cnt]=(EDGE){head[u],v};head[u]=cnt;}
void build(int l,int r,int o)
{
    if(l==r){sum[o]=wn[l];return;}
    int mid=(l+r)>>1;
    build(lson);build(rson);
    sum[o]=sum[o<<1]+sum[o<<1|1];
}
void update(int pre,int val,int l,int r,int o)
{
    if(l==r){sum[o]=val;return;}
    int mid=(l+r)>>1;
    if(pre<=mid) update(pre,val,lson);
    else update(pre,val,rson);
    sum[o]=sum[o<<1]+sum[o<<1|1];
}
int query(int L,int R,int l,int r,int o)
{
    if(L<=l&&r<=R) return sum[o];
    int mid=(l+r)>>1,ans=0;
    if(L<=mid) ans+=query(L,R,lson);
    if(R> mid) ans+=query(L,R,rson);
    return ans;
}
int query_tree(int u,int v)
{
    int ans=0;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        ans+=query(id[top[u]],id[u],1,n,1);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    return ans+query(id[u],id[v],1,n,1);
}
void dfs1(int u,int ff)
{
    fa[u]=ff;dep[u]=dep[ff]+1;size[u]=1;
    int maxson=0;
    for(int i=head[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to) if(v!=ff)
    {
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>maxson) maxson=size[v],son[u]=v;
    }
}
void dfs2(int u,int topf)
{
    id[u]=++tim;top[u]=topf;wn[tim]=ww[u];
    if(!son[u]) return;
    dfs2(son[u],topf);
    for(int i=head[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to) if(v!=fa[u]&&v!=son[u])
        dfs2(v,v);
}
int main()
{
//    freopen("input","r",stdin);
//    freopen("output","w",stdout);
    xu=n=read(),q=read();
    for(int i=1;i<=n;i++) fe[i]=i;
    for(int i=1;i<=n;i++)
    {
        u=read(),v=read(),w=read();
        t1=getf(u),t2=getf(v);
        if(t1!=t2)
        {
            fe[t1]=t2;xu=n+i;ww[xu]=w;
            add(u,xu);add(xu,u);
            add(v,xu);add(xu,v);
        }
        else {keu=u;kev=v;kew=w;kex=i;}
    }
    n<<=1;
    dfs1(1,1);dfs2(1,1);build(1,n,1);
    int ans=0;
    while(q--)
    {
        op=read(),u=read(),v=read();
        if(op==1)
        {
            if(u==kex) kew=v;
            else update(id[u+(n>>1)],v,1,n,1);
        }
        else
        {
            ans=query_tree(u,v);
            printf("%d
",min(ans,kew+min(query_tree(u,keu)+query_tree(v,kev),query_tree(u,kev)+query_tree(v,keu))));
        }
    }
    return 0;
}

 

以上是关于luogu P4949 最短距离的主要内容,如果未能解决你的问题,请参考以下文章

[luogu4162 SCOI2009] 最长距离(最短路)

luogu P4162 [SCOI2009]最长距离

Luogu 1429 平面最近点对 | 平面分治

[最短路-Floyd][数学]Luogu P1552 牛的旅行

[Luogu P1613]跑路 (DP+倍增+最短路)

[Luogu P3953] 逛公园 (最短路+拓扑排序+DP)