【题解】Luogu CF1051F The Shortest Statement

原题传送门:CF1051F The Shortest Statement

题目大意,给你一个稀疏图,q次查询,查询两点之间距离

边数减点小于等于20

这不是弱智题吗,23forever dalao又开始虐题

作为蒟蒻的我只能在一旁出售烤绿鸟和main包,和大家一起吃西瓜

仔细想想,这题的确是很弱智

先随便找一个生成树,这样就能跑lca了

剩下的几条边的端点跑一下SPFA堆优化dij,用于特判,SPFA已经死了

查询先用lca算一下距离,再暴力枚举这40个端点到两点的距离值和(最多)

就这样完了,没错

程序中还有一些优化,看细节来体会(这题真的弱智)

奉上蒟蒻的代码

#pragma GCC optimize("O3")
#include <bits/stdc++.h>
#define N 300005
#define Logn 19
#define M 21
#define inf 1e18
#define ll long long
using namespace std;
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
inline ll Min(register ll a,register ll b)
{
    return a<b?a:b;
}
int n,m,q;
struct edge{
    int to,next,w;
}e[N<<1];
int head[N],cnt=0;
set< pair<int,int> >bad;
int tin[N],tout[N],T;
bool u[N];
ll h[N],d[M<<1][N];
int p[Logn][N];
inline void add(register int u,register int v,register int w)
{
    e[++cnt]=(edge){v,head[u],w};
    head[u]=cnt;
}
inline void dfs(register int v,register int pr)
{
    tin[v]=T++;
    p[0][v]=pr;
    u[v]=true;
    for(register int i=1;i<Logn;++i)
        p[i][v]=p[i-1][p[i-1][v]];
    for(register int i=head[v];i;i=e[i].next)
        if(!u[e[i].to])
        {
            h[e[i].to]=h[v]+e[i].w;
            dfs(e[i].to,v);
            if(v<e[i].to)
                bad.erase(make_pair(v,e[i].to));
            else
                bad.erase(make_pair(e[i].to,v));
        }
    tout[v]=T;
}
inline bool isAncestor(register int a,register int b)
{
    return tin[a]<=tin[b]&&tout[a]>=tout[b];
}
inline int LCA(register int a,register int b)
{
    if(isAncestor(a,b))
        return a;
    if(isAncestor(b,a))
        return b;
    for(register int i=Logn-1;i>=0;--i)
        if(!isAncestor(p[i][a],b))
            a=p[i][a];
    return p[0][a];
}
inline void dij(register int st,register ll d[N])
{
    set<pair<ll,int> > q;
    for(register int i=0;i<n;++i)
        d[i]=inf;
    d[st]=0;
    q.insert(make_pair(d[st],st));
    while(!q.empty())
    {
        int v=q.begin()->second;
        q.erase(q.begin());
        for(register int i=head[v];i;i=e[i].next)
            if(d[e[i].to]>d[v]+e[i].w)
            {
                q.erase(make_pair(d[e[i].to],e[i].to));
                d[e[i].to]=d[v]+e[i].w;
                q.insert(make_pair(d[e[i].to],e[i].to));
            }
    }
}
int main()
{
    n=read(),m=read();
    for(register int i=1;i<=m;++i)
    {
        int u=read(),v=read(),w=read();
        --u,--v;
        add(u,v,w),add(v,u,w);
    }
    for(register int v=0;v<n;++v)
        for(register int i=head[v];i;i=e[i].next)
            if(v<e[i].to)
                bad.insert(make_pair(v,e[i].to));
    dfs(0,0);
    int cpos=0;
    int siz=bad.size();
    while(!bad.empty())
        dij(bad.begin()->first,d[cpos++]),bad.erase(bad.begin());
    q=read();
    while(q--)
    {
        int u=read(),v=read();
        --u,--v;
        int lca=LCA(u,v);
        ll ans=h[u]+h[v]-2*h[lca];
        for(register int i=0;i<siz;++i)
            ans=Min(ans,d[i][u]+d[i][v]);
        printf("%lld\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/9741026.html

时间: 2024-10-09 11:57:19

【题解】Luogu CF1051F The Shortest Statement的相关文章

CF1051F The Shortest Statement

题目大意:给定一张\(n\)个点\(m\)条有权边的无向联通图,\(q\)次询问两点间的最短路 \(n\le100000\),\(m\le100000\),\(1\le100000\),\(m\)-\(n\le20\). 输入样例#1: 3 3 1 2 3 2 3 1 3 1 5 3 1 2 1 3 2 3 输出样例#1: 3 4 1 输入样例#2: 8 13 1 2 4 2 3 6 3 4 1 4 5 12 5 6 3 6 7 8 7 8 7 1 4 1 1 8 3 2 6 9 2 7 1 4

The Shortest Statement CodeForces - 1051F(待测试)

#include <iostream> #include <cstdio> #include <sstream> #include <cstring> #include <map> #include <set> #include <vector> #include <stack> #include <queue> #include <algorithm> #include <cma

Educational Codeforces Round 51 (Rated for Div. 2) The Shortest Statement

题目链接:The Shortest Statement 今天又在群里看到一个同学问$n$个$n$条边,怎么查询两点直接最短路.看来这种题还挺常见的. 为什么最终答案要从42个点的最短路(到$x,y$)之和,还有$x,y$到$LCA(x,y)$的距离里面取呢? 就是如果走非树边,那么一定要走42个点中的一个,不走树边,就是LCA求了. 我写的太蠢了,还写生成树,看大家都是LCA中的dfs直接标记下就行了. 验证了算法的正确,我又试了试把每条非树边只加一个点,也是AC的,其实想了想,确实正确. 1

题解 luogu P1850 【换教室】

题解 luogu P1850 [换教室] 时间:2019.8.6 一晚上(约 3.5h 写完) 题目描述 对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程. 在可以选择的课程中,有 \(2n\) 节课程安排在 \(n\) 个时间段上.在第 \(i\)(\(1 \leq i \leq n\))个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 \(c_i\) 上课,而另一节课程在教室 \(d_i\) 进行. 在不提交任何申请的情况下,学生们需要

题解 luogu P5021 【赛道修建】

题解 luogu P5021 [赛道修建] 时间:2019.8.9 20:40 时间:2019.8.12 题目描述 C 城将要举办一系列的赛车比赛.在比赛前,需要在城内修建 \(m\) 条赛道. C 城一共有 \(n\) 个路口,这些路口编号为 \(1,2,\dots,n\),有 \(n-1\) 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口.其中,第 \(i\) 条道路连接的两个路口编号为 \(a_i\) 和 \(b_i\),该道路的长度为 \(l_i\).借助这 \(n-1\) 条

题解 Luogu P2499: [SDOI2012]象棋

关于这道题, 我们可以发现移动顺序不会改变答案, 具体来说, 我们有以下引理成立: 对于一个移动过程中的任意一个移动, 若其到达的位置上有一个棋子, 则该方案要么不能将所有棋子移动到最终位置, 要么可以通过改变顺序使这一次移动合法 证明: 考虑到达位置上的那个棋子, 如果它没有到达最终位置, 则我们考虑将该棋子移至下一步, 如果下一步还有没有到达最终位置的棋子, 则也移动它 否则直接调换这两个棋子的移动顺序即可 好的我们去除了题目中的要求: 「移动过程中不能出现多颗棋子同时在某一格的情况」, 接

题解 Luogu P3370

讲讲这题的几种做法: 暴力匹配法 rt,暴力匹配,即把字符串存起来一位一位判相等 时间复杂度$ O(n^2·m) $ 再看看数据范围 $n\le10^5,m\le10^3$ 当场爆炸.当然有暴力分 代码(20pts): #include <bits/stdc++.h> using namespace std; char c[100001][1001]; bool pd(int x, int y) { int l1 = strlen(c[x]), l2 = strlen(c[y]); if(l1

题解 luogu P1501【[国家集训队]Tree II】(Link-Cut-Tree)

Link-Cut-Tree 的懒标记下传正确食用方法. 我们来逐步分析每一个操作. 1:+ u v c:将u到v的路径上的点的权值都加上自然数c; 解决方法: 很显然,我们可以 split(u,v) 来提取u,v这一段区间,提取完了将 Splay(v),然后直接在v上打加法标记add即可. 代码: inline void pushadd(ll x,ll val){//打标记 s[x]+=sz[x]*val,v[x]+=val,add[x]+=val; s[x]%=MOD,v[x]%=MOD,ad

[题解] luogu P1985 [USACO07OPEN]翻转棋

题面 今天学搜索,正好水一发以前做的这道毒瘤题 话说这道题做了我一天,别人都是各种优化,不超100行 天真的我硬核刚了220行,全程0优化水过 但其实不用这么长,我有的函数写的有点重复了( 思路: 显然是dfs,一行一行的来 搜到[i, j]时(i > 1),看[i - 1, j]是否为黑,是的话就翻转[i, j], 也就是说搜完当前行就要保证上一行的棋全都翻成了白色 当搜到最后一行时, 既要保证上一行翻成白色,还要保证自己也都翻成白色, 最后还要特判一下最后两个的翻转. 当时年少轻狂,我想着层