BZOJ.2125.最短路(仙人掌 最短路Dijkstra)

题目链接

多次询问求仙人掌上两点间的最短路径。
如果是在树上,那么求LCA就可以了。
先做着,看看能不能把它弄成树。
把仙人掌看作一个图(实际上就是),求一遍根节点到每个点的最短路dis[i]。
对于u,v,若w=LCA(u,v)不在环上(u,v不同在一个环),那么dis(u,v)可以像在树上一样直接求得。
若w=u||w=v,(且w不是环内的一个点),dis(u,v)=dis[u/v]-dis[w]。
如果w在环上,那么设x,y为u,v往上能到的最近的环上的两个点,那么dis(u,v)=dis[u]-dis[x]+dis[v]-dis[y]+(x,y在环上的最短距离)。
设cdis[i]为按DFS序得到的根节点到i的距离,len[i]为环i的长,x,y在环上的最短距离就是min(abs(cdis[x]-cdis[y]),len[bel]-abs(cdis[x]-cdis[y]))。
怎么把它弄成一棵树 求LCA?
所有在一个环上的点以这个环深度最小的点作为父节点,由父节点向子节点连边就行了。之前缩一下点。

求仙人掌上单源最短路可以O(n)DP
太弱不会。。

//6492kb    376ms
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define mp std::make_pair
#define pr std::pair<int,int>
const int N=1e4+5,M=2e5+5;

int n,m,Q,Enum,H[N],nxt[M<<1],to[M<<1],val[M<<1],dis[N],cnt,bel[N],len[N],cdis[N],dep[N],fa[N][16];
std::priority_queue<pr> q;
bool vis[N],vis2[N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
inline void AddEdge(int u,int v,int w){
    to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, val[Enum]=w;
    to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum, val[Enum]=w;
}
void Dijkstra()
{
    memset(dis,0x3f,sizeof dis);
    dis[1]=0, q.push(mp(0,1));
    while(!q.empty())
    {
        int x=q.top().second; q.pop();
        if(vis2[x]) continue;
        vis2[x]=1;
        for(int i=H[x]; i; i=nxt[i])
            if(dis[to[i]]>dis[x]+val[i])
                dis[to[i]]=dis[x]+val[i], q.push(mp(-dis[to[i]],to[i]));
    }
}
void DFS(int x)
{
    vis[x]=1;
    for(int v,i=H[x]; i; i=nxt[i])
        if(to[i]!=fa[x][0]&&!bel[to[i]])
            if(!vis[v=to[i]]) fa[v][0]=x,cdis[v]=cdis[x]+val[i],dep[v]=dep[x]+1,DFS(v);
            else{//find a circle v
                len[++cnt]=cdis[x]-cdis[v]+val[i];
                for(int j=x,pre; j!=v; j=pre)
                    bel[j]=cnt, pre=fa[j][0], fa[j][0]=v;
                //不给bel[v]赋值,因为v可能作为多个环的交点。
            }
}
void Init_LCA()
{
    for(int x=2; x<=n; ++x)
//      for(int i=1; i<16&&dep[x]>=(1<<i); ++i)
        for(int i=1; i<16; ++i)
            fa[x][i]=fa[fa[x][i-1]][i-1];
}
void Query_LCA(int &u,int &v)
{
    if(dep[u]<dep[v]) std::swap(u,v);
    for(int i=15; ~i; --i)
        if(dep[fa[u][i]]>=dep[v]) u=fa[u][i];//这样dep[1]=1!
    if(u==v) return;
    for(int i=15; ~i; --i)
        if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
//  u=fa[u][0], v=fa[v][0];
}
void DFS_for_Deep(int x){
    for(int i=H[x]; i; i=nxt[i])
        if(to[i]!=fa[x][0]) dep[to[i]]=dep[x]+1, DFS_for_Deep(to[i]);
}

int main()
{
    n=read(),m=read(),Q=read();
    for(int u,v,i=1; i<=m; ++i) u=read(),v=read(),AddEdge(u,v,read());
    Dijkstra(), DFS(1);
    Enum=0, memset(H,0,sizeof H);
    for(int i=2; i<=n; ++i) AddEdge(fa[i][0],i,0);
    dep[1]=1, DFS_for_Deep(1)/*在新树上得到dep啊。。*/, Init_LCA();
    int u,v,x,y,l;
    while(Q--)
    {
        u=read(),v=read(),Query_LCA(x=u,y=v);//此时的x,y若在环上,则是最接近u,v的环上的点(再往上跳一步就是LCA,环深度最低的点)
        if(x!=y && bel[x] && bel[x]==bel[y])//x!=y即u,v不是在一条链上 //注意判bel[x]!=0!
            printf("%d\n",dis[u]+dis[v]-dis[x]-dis[y]+std::min(std::abs(cdis[x]-cdis[y]),len[bel[x]]-std::abs(cdis[x]-cdis[y])));
        else if(x==y) printf("%d\n",dis[u]+dis[v]-(dis[x]<<1));
        else printf("%d\n",dis[u]+dis[v]-(dis[fa[x][0]]<<1));//不在一个环上也直接更新。
    }
    return 0;
}

原文地址:https://www.cnblogs.com/SovietPower/p/8971808.html

时间: 2024-10-31 12:24:32

BZOJ.2125.最短路(仙人掌 最短路Dijkstra)的相关文章

BZOJ 2125 最短路 仙人掌最短路

为了证明我还活着所以我发了篇博客_ (:з」∠) _ 题意: 给定一张仙人掌图,n<=10000,多次询问两点间最短路. Q<=10000. 解析: 首先如果这是一棵树的话,那么我们只需要选定一个根,之后扫一遍这棵树,询问的话即是两点到根节点的距离之和减去二倍的两点lca到根节点距离. 那么如果是一棵仙人掌的话,我们强行套用这个办法,重新构造一棵树. 对于仙人掌中的一个环来说,我们把该环中深度最小的点当做这个环的根,然后环上其他点连向该环,非环上边正常连接. 这个树有什么优越性呢? 不妨假定1

hdu1688(dijkstra求最短路和次短路)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1688 题意:第k短路,这里要求的是第1短路(即最短路),第2短路(即次短路),以及路径条数,最后如果最短路和次短路长度差1,则输出两种路径条数之和,否则只输出最短路条数. 思路:dijkstra变形,注意状态的转移,代码上附了注释,就不多说了.. 代码: 1 #include <bits/stdc++.h> 2 #define MAXN 1010 3 using namespace std; 4

HDU 2544:最短路( 最短路径入门 &amp;&amp;Dijkstra &amp;&amp; floyd )

最短路 Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 30972    Accepted Submission(s): 13345 Problem Description 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找

HDU 2544 最短路(我的dijkstra算法模板、SPAFA算法模板)

思路:这道题是基础的最短路径算法,可以拿来试一下自己对3种方法的理解 dijkstra主要是从第一个点开始枚举,每次枚举出当当前最小的路径,然后再以那最小的路径点为起点,求出它到其它未标记点的最短距离 bellman-ford 算法则是假设有向网中有n 个顶点.且不存在负权值回路,从顶点v1 和到顶点v2 如果存在最短路径,则此路径最多有n-1 条边.这是因为如果路径上的边数超过了n-1 条时,必然会重复经过一个顶点,形成回路:而如果这个回路的权值总和为非负时,完全可以去掉这个回路,使得v1到v

最短路知识点总结(Dijkstra,Floyd,SPFA,Bellman-Ford)

Dijkstra算法: 解决的问题: 带权重的有向图上单源最短路径问题.且权重都为非负值.如果采用的实现方法合适,Dijkstra运行时间要低于Bellman-Ford算法. 思路: 如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点.那么(Vi...Vk)也必定是从i到k的最短路径.为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法.譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最短的顶点Vi,那么当前已知可得从V0到达Vj

BZOJ 1922 大陆争霸(最短路)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1922 题意:有向图求1到n的最短路.但是有些点在某些点被遍历之后才能走. 思路:没有限制就是普通的最短路.多了限制再增加一个数组记录被限制的点由于限制因素而最早可被遍历时间.这样每次两个时间的最大值就是这个点可以被遍历的最早时间. int g[N][N],d1[N],d2[N],num[N]; bool a[N][N],visit[N]; int n,m; void Dij() { i

最短路和次短路问题,dijkstra算法

1 /* 2  *题目大意: 3  *在一个有向图中,求从s到t两个点之间的最短路和比最短路长1的次短路的条数之和; 4  * 5  *算法思想: 6  *用A*求第K短路,目测会超时,直接在dijkstra算法上求次短路; 7  *将dist数组开成二维的,即dist[v][2],第二维分别用于记录最短路和次短路; 8  *再用一个cnt二维数组分别记录最短路和次短路的条数; 9  *每次更新路径的条数时,不能直接加1,,应该加上cnt[u][k],k为次短路径或者最短路径的标记; 10  *

BZOJ3040 最短路 (堆优化dijkstra)

这题不是裸的最短路么?但是一看数据范围就傻了.点数10^6,边数10^7.这个spfa就别想了(本来spfa就是相当不靠谱的玩意),看来是要用堆优化dijkstra了.但是,平时写dijkstra时为了偷懒直接用的STL的priority_queue,没办法改变权值,所以都是直接把pair压进堆里.然后时间复杂度O(mlogm),空间复杂度O(m),不靠谱.手写二叉堆?改变权值是O(logn)的,所以时间复杂度O(mlogn),空间复杂度O(n),还是要T.看来是需要一种比较牛逼的堆了. Fib

次短路 + 第K短路 模版

虽然从字面上看,次短路和第2短路是一样的.但是我在题目中遇到的却不是这样的. 在有些题目中,需要判断次短路是否存在.比如说,u.v之间只有一条路径.那么只有最短路.次短路是不存在的.这时候,解题方法是先求出最短路,然后枚举删除最短路径中的边,然后求最小值.题目可以看poj3986. 第K短路的实现是 SPFA + A* 算法. A*算法通过一个估价函数f(h)来估计途中的当前点p到终点的距离,并由此决定它的搜索方向,当这条路径失败时,它会尝试其他路径.对于A*,估价函数 = 当前值 + 当前位置