【JZOJ1914】【2011集训队出题】最短路

题目大意
给你一个带权无向图,满足图上任意一条边最多属于一个环,有\(q\)个询问,求\(u,v\)之间的最短路。
\(n,q\leq 10000\)

Solution

首先用Tarjan建一棵以\(1\)为根的搜索树,找出每个环,记录环的总长,将环内每个点\(u\)连向环内\(dfs\)序最小的点\(v\),边权为\(u\)到\(v\)的最短路,然后把不在环上的边照旧连上,这样我们就得到了一棵树。
现在要求\(a\)到\(b\)的最短路,我们考虑倍增,若两个点在一条链上,它们的最短路就是树上的距离。若两个点不在一条链上,设\(u\)和\(v\)分别为\(a\)和\(b\)到\(lca\)的链上\(lca\)下面那个点,若\(u,v\)在原图中不在一个环,\(a,b\)的最短路就是它们在树上的距离,若\(u,v\)在原图中在一个环,则\(u\)到\(v\)有两种走法,我们要取最小值,然后再加上\(a\)到\(u\)的距离和\(b\)到\(v\)的距离。
这里有个小技巧,我们可以先在原图上从\(1\)开始求出\(1\)到每个点的最短路\(dis[i]\),那么将环上每个点\(u\)连向\(dfs\)序最小的点\(v\)时,边权就是\(dis[u]-dis[v]\),后面求树上两点距离时也是用\(dis\)数组求。

Code

#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;
const int N=10007,M=600007;
ll Abs(ll a){return a>0?a:-a;}

int n,m,q;

int tot,cnt,st[N],to[M],nx[M],dep[N],col[N];
ll len[M],dis[N],sum[N],totlen[N];
vector<int> son[N];
void add(int u,int v,ll w){to[++tot]=v,nx[tot]=st[u],len[tot]=w,st[u]=tot;}

int head,tail,que[M],vis[N];
void spfa(){
    memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));
    head=1,que[tail=1]=1,dis[1]=0,vis[1]=1;
    while(head<=tail){
        int u=que[head++];vis[u]=0;
        for(int i=st[u];i;i=nx[i])if(dis[u]+len[i]<dis[to[i]]){
            dis[to[i]]=dis[u]+len[i];
            if(!vis[to[i]])que[++tail]=to[i],vis[to[i]]=1;
        }
    }
}

int tid,dfn[N],anc[N][15];
void dfs(int u,int from){
    dfn[u]=++tid;
    for(int i=st[u];i;i=nx[i]){
        if(!dfn[to[i]])anc[to[i]][0]=u,sum[to[i]]=sum[u]+len[i],dfs(to[i],u);
        else if(to[i]!=from&&dfn[to[i]]<dfn[u]){
            ++cnt,totlen[cnt]=sum[u]-sum[to[i]]+len[i];
            for(int j=u,t;j!=to[i];)son[to[i]].push_back(j),col[j]=cnt,t=anc[j][0],anc[j][0]=to[i],j=t;
        }
    }
    if(anc[u][0]==from)son[from].push_back(u);
}

void dfs1(int u){
    for(int i=0;i<son[u].size();++i)dep[son[u][i]]=dep[u]+1,dfs1(son[u][i]);
}

ll qry(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    int a=u,b=v;
    for(int i=14;i>=0;--i)if(dep[anc[u][i]]>=dep[v])u=anc[u][i];
    if(u==v)return dis[a]-dis[b];
    for(int i=14;i>=0;--i)if(anc[u][i]!=anc[v][i])u=anc[u][i],v=anc[v][i];
    if(col[u]&&col[u]==col[v])return dis[a]-dis[u]+dis[b]-dis[v]+min(Abs(sum[u]-sum[v]),totlen[col[u]]-Abs(sum[u]-sum[v]));
    return dis[a]+dis[b]-2*dis[anc[u][0]];
}

int main(){
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=m;++i){
        int u,v;ll w;
        scanf("%d%d%lld",&u,&v,&w),add(u,v,w),add(v,u,w);
    }
    spfa();
    dfs(1,1);
    dep[1]=1,dfs1(1);
    for(int j=1;j<=14;++j)for(int i=1;i<=n;++i)anc[i][j]=anc[anc[i][j-1]][j-1];
    while(q--){
        int u,v;
        scanf("%d%d",&u,&v);
        printf("%lld\n",qry(u,v));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zjlcnblogs/p/12088697.html

时间: 2024-09-30 17:15:06

【JZOJ1914】【2011集训队出题】最短路的相关文章

JZOJ 1981. 【2011集训队出题】Digit

JZOJ 1981. [2011集训队出题]Digit Time Limits: 1000 ms Memory Limits: 128000 KB Description 在数学课上,小T又被老师发现上课睡觉了.为了向全班同学证明小T刚才没有好好听课,数学老师决定出一道题目刁难一下小T,如果小T答不出,那么-- 情节就按照俗套的路线发展下去了,小T显然无法解决这么复杂的问题,可怜的小T只能向你求助: 题目是这样的: 求一个满足条件的n位数A(不能有前导0),满足它的数字和为s1,并且,A*d的数

【2010集训队出题】小Z的袜子

Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子.当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己

[JZOJ1904] 【2010集训队出题】拯救Protoss的故乡

题目 题目大意 给你一个树形的网络,每条边从父亲流向儿子.根节点为原点,叶子节点流向汇点,容量为无穷大. 可以给一些边扩大容量,最多总共扩大\(m\)容量.每条边的容量有上限. 求扩大容量后最大的最大流. 思考历程 隐隐约约地猜到正解跟树链剖分有什么关系,可是没有打,也没有时间打. 只能暴力DP来水分. 设\(h_{i,j}\)为\(i\)的父亲到\(i\)的最大流,扩大了\(j\)次容量.\(g_{i,j}\)为\(i\)到子树的最大流,扩大了\(j\)次容量.前者由后者和边的容量取最小值后得

【JZOJ1899】【2010集训队出题】剪枝

题目大意 给出一个有根树,\(1\)为根,若某个节点的儿子全是叶子,你可以将该节点的儿子全部剪掉,这样的操作可以进行多次.定义这棵树的价值为:将树上所有叶子按照\(dfs\)序排序后,所有叶子点权之和-相邻两叶子路径上点权最大值.现在你要通过剪枝使得这棵树价值最大. \(n\leq 100000\) 分析 设\(f_i\)表示\(i\)作为最后一个叶子时的最大价值.暴力枚举原树(没有剪枝)相邻的两个叶子,显然左链上每个点的\(f\)都可以转移到右链上,我们暴力处理出这条路径,分类讨论点权最大值在

[JZOJ1901] 【2010集训队出题】光棱坦克

题目 题目大意 给你个平面上的一堆点,问序列\({p_i}\)的个数. 满足\(y_{p_{i-1}}>y_{p_i}\)并且\(x_{p_i}\)在\(x_{p_i-1}\)和\(x_{p_i-2}\)之间. 正解 我不知道为什么我的树状数组打挂了--尽管不一定能AC,但是WA了-- 这题的正解有很多,最为传奇的,则是彭大爷的神仙解法. 显然这是个DP,而他抛弃了按照\(y\)从大到小排序的传统做法,反而是以\(x\)从小到大排序.将\({p_i}\)倒过来做.设\(f_{i,0/1}\)表示

圆方树小结

圆方树 jzoj 1914. [2011集训队出题]最短路 这是道圆方树+倍增LCA裸题. 圆方树,顾名思义,就是圆点和方点所组成的树. 而方点就是一个圆的根,一般都是\(dfs\)时第一个到这个圆的那个位置,然后另附一个点当做方点.然后圆所组成的点都连向方点. 而对于这种圆方边的边权,则为它到根的最近值. 从而将一个仙人掌转成了一棵树. 然后对于这棵树,我们就可以用倍增来求出两两点之间的最短路了. 注意的是,对于最后走到的位置,我们要看看它是从上面绕近还是直接从下面走更优!!! 就这样子了.

国家集训队2011 happiness

[试题来源] 2011中国国家集训队命题答辩 [问题描述] 高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,那么他们又将收获一些喜悦值.作为计算机竞赛教练的scp大老板,想知道如何分配可以使得全班的喜悦值总和最大. [输入格式] 第一行两个正整数n,m.接下来是六个矩阵第一个矩阵为n行m列 此矩阵的第i行第j列的数字表示座位在第i行第j列的同学

cogs 1901. [国家集训队2011]数颜色

Cogs 1901. [国家集训队2011]数颜色 ★★★   输入文件:nt2011_color.in   输出文件:nt2011_color.out   简单对比时间限制:0.6 s   内存限制:512 MB [试题来源] 2011中国国家集训队命题答辩 [问题描述] 墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问.墨墨会像你发布如下指令:1. Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔.2. R P Col 把第P支画笔替换为

国家集训队2011]happiness(吴确)

1873. [国家集训队2011]happiness(吴确) ★★★   输入文件:nt2011_happiness.in   输出文件:nt2011_happiness.out   简单对比时间限制:1 s   内存限制:512 MB [试题来源] 2011中国国家集训队命题答辩 [问题描述] 高一一班的座位表是个n*m的矩阵,经过一个学期的相处,每个同学和前后左右相邻的同学互相成为了好朋友.这学期要分文理科了,每个同学对于选择文科与理科有着自己的喜悦值,而一对好朋友如果能同时选文科或者理科,