首页 > 其他分享 >CF1051F题解

CF1051F题解

时间:2024-10-03 16:12:40浏览次数:8  
标签:dep int 题解 void CF1051F fa th dis

传送门:https://codeforces.com/problemset/problem/1051/F

注意到\(m-n\le 20\),求一个连通图中任意两点间最短路,我们不难想到将问题转换到树上。先求出树的任意一颗生成树,此时倍增或者树刨能轻松算出仅含树边的最小路径。

而对于非树边,从边的角度显然很难做到,我们不妨从点的角度思考,在非树边上的点至多有\(42\)个,我们不妨假设这些点是必经点,我们以这\(42\)个点为起点分别求出单源最短路,然后分最短路上是否存在非树边连接的点来讨论,如果存在就从特殊点出发求最短路,否则直接在树上求两点距即可,最后答案就是两者较小值。时间复杂度\(O((n+q)log_{2}n+42(n+m)logn)\)。

PS:这道题几乎用到了图论一半的知识,是道非常好的练手题。

#include <bits/stdc++.h>

#define int long long
using namespace std;

inline int read() {
    char c;
    bool flag = false;
    while ((c = getchar()) < '0' || c > '9') if (c == '-') flag = true;
    int res = c - '0';
    while ((c = getchar()) >= '0' && c <= '9') res = (res << 3) + (res << 1) + c - '0';
    return flag ? -res : res;
}

const int N = 1e5 + 1;

struct Edge {
    int v, next, w, u;
} e[N << 1], te[N << 1];

int h[N], th[N], c, tc, fa[N][21], d[N][21], dis[43][N], dep[N], vis[N], ff[N];
bool is_key[N];
vector<int> key;

void add(int u, int v, int w) {
    e[c].u = u;
    e[c].v = v;
    e[c].w = w;
    e[c].next = h[u];
    h[u] = c++;
}

void tadd(int u, int v, int w) {
    te[tc].v = v;
    te[tc].w = w;
    te[tc].next = th[u];
    th[u] = tc++;
}

void dfs(int x, int f) {
    dep[x] = dep[f] + 1;
    fa[x][0] = f;
    for (int i = 1; i <= 20; ++i) fa[x][i] = fa[fa[x][i - 1]][i - 1], d[x][i] = d[x][i - 1] + d[fa[x][i - 1]][i - 1];
    for (int i = th[x]; ~i; i = te[i].next) {
        int y = te[i].v, w = te[i].w;
        if (y == f) continue;
        d[y][0] = w;
        dfs(y, x);
    }
}

int getDis(int x, int y) {
    int ans = 0;
    if (dep[x] > dep[y]) swap(x, y);
    for (int i = 20; i >= 0; --i) {
        if (dep[fa[y][i]] >= dep[x]) ans += d[y][i], y = fa[y][i];
    }
    if (x == y) return ans;
    for (int i = 20; i >= 0; --i) {
        if (fa[x][i] != fa[y][i]) ans += d[x][i] + d[y][i], x = fa[x][i], y = fa[y][i];
    }
    return ans + d[x][0] + d[y][0];
}

void Dijkstra(int s) {
    memset(vis, 0, sizeof vis);
    priority_queue<pair<int, int>> q;
    q.push(make_pair(0, key[s]));
    dis[s][key[s]] = 0;
    while (!q.empty()) {
        int x = q.top().second;
        q.pop();
        if (vis[x]) continue;
        vis[x] = 1;
        for (int i = h[x]; ~i; i = e[i].next) {
            int y = e[i].v;
            if (dis[s][x] + e[i].w < dis[s][y]) {
                dis[s][y] = dis[s][x] + e[i].w;
                q.push(make_pair(-dis[s][y], y));
            }
        }
    }
}

int get(int x) { return x == ff[x] ? x : ff[x] = get(ff[x]); }

signed main() {
    memset(h, -1, sizeof h);
    memset(th, -1, sizeof th);
    memset(dis, 0x3f, sizeof dis);
    int n = read(), m = read();
    for (int i = 1; i <= n; ++i) ff[i] = i;
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read(), w = read();
        add(u, v, w);
        add(v, u, w);
    }
    for (int i = 0; i < c; i += 2) {
        if (get(e[i].u) != get(e[i].v)) tadd(e[i].u, e[i].v, e[i].w), tadd(e[i].v,e[i].u,e[i].w), ff[get(e[i].u)] = get(e[i].v);
        else {
            if (!is_key[e[i].u]) key.push_back(e[i].u);
            if (!is_key[e[i].v]) key.push_back(e[i].v);
            is_key[e[i].u] = is_key[e[i].v] = true;
        }
    }
    dep[1] = 1;
    dfs(1, 0);
    for (int i = 0; i < key.size(); ++i) Dijkstra(i);
    int q = read();
    while (q--) {
        int u = read(), v = read();
        int ans = getDis(u, v);
        for (int i = 0; i < key.size(); ++i) ans = min(ans, dis[i][u] + dis[i][v]);
        printf("%lld\n", ans);
    }
    return 0;
}

标签:dep,int,题解,void,CF1051F,fa,th,dis
From: https://www.cnblogs.com/Jefferyz/p/18445751

相关文章

  • 题解:CF724E Goods transportation
    可以在cnblog中阅读。题意有\(n\)座城市,第\(i\)座城市生产了\(p_i\)件货物,最多可以出售\(s_i\)件货物,编号小的城市可以向编号大的城市运输至多\(c\)件货物,问最多能出售多少货物。\(n\le10^4\)。分析乍一看是一个网络流问题,可以这样建图,令\(S\)为源点\(T\)......
  • ABC221G Jumping Sequences 题解
    JumpingSequences把移动的上下左右改成左上、左下、右上、右下(坐标轴旋转\(45\)°)。则最终目的地是\((A+B,A-B)\)。(以前移动的方式是\((\pmd_i,0),(0,\pmd_i)\)。现在每次移动的方式是\((\pmd_i,\pmd_i)\))则\(x,y\)两维可以分开考虑。目标:从\(d_1\simd_n\)中选......
  • Codeforces Round 976 (Div. 2) 题解
    CodeforcesRound976(Div.2)题解2020B一个常见的想法:开关灯=异或,虽然在这道题里没啥用注意到,第i盏灯的按钮被按的次数就是i的除它本身以外的因子个数而完全平方数的因子数为奇数,其他数的因子数为偶数点击查看更多信息#include<bits/stdc++.h>usingnamespacestd;voi......
  • 题解:CF1976D Invertible Bracket Sequences
    可以在cnblog中阅读。题意给一个合法括号序列,问有多少区间\([l,r]\),使得将区间内的每个括号翻转后,括号序列仍合法。分析十分套路地,我们将(看成\(+1\),将)看成\(-1\),则一个括号序列合法的充要条件是转换后的序列满足:前缀和任意位置非负;最后一项为\(0\)。考虑翻转......
  • ZZJC新生训练赛第二场题解
    先给出比赛链接:https://ac.nowcoder.com/acm/contest/92036A小红打怪ShowCodeAclassPoint{//点类public:intx,y;Point(){}Point(intx,inty):x(x),y(y){}Pointoperator+(constPoint&P)const{returnPoint(x+P.x,y+P.y);......
  • 题解:TopCoder12316 ThreeColorability
    Vjudge可以出成《三色绘恋》背景。题意给一个格点数为\((n+1)\times(m+1)\)的网格,给格点染色,相邻的格点不能染成同样的颜色。每个格子有一条对角线的边,可选N形和Z形。现在有一个残缺的网格,存在一些格子的对角线连法不确定,构造一种字典序最小的方案使得至少存在一种染色......
  • 树上最值路径 题解
    题意简述给你一棵\(n\)个结点的树,编号为\(1\simn\),求有多少路径\(\operatorname{Path}(u\rightarrowv)\),满足\(u=\max\{x\midx\in\operatorname{Path}(u\rightarrowv)\}\),\(v=\min\{x\midx\in\operatorname{Path}(u\rightarrowv)\}\)。......
  • 题解:CF2014D Robert Hood and Mrs Hood
    差分,每次差分将\(\max(1,l-d+1)\)加\(1\),将\(r+1\)位置减\(1\)。然后前缀和求原数组,再遍历一下求最小值和最大值即可。代码:#include<bits/stdc++.h>usingnamespacestd;intmain(){ intt; cin>>t; while(t--){ intn,d,k; cin>>n>>d>>k;......
  • 题解:CF2009E Klee's SUPER DUPER LARGE Array!!!
    设\(m\)为\(a_1+\dots+a_i\),\(n\)为\(a_{i+1}+\dots+a_n\)。我们可以使用二分查找来搜索\(i\),使得\(m-n\)为最小的负数。如果我们移动到\(i+1\),则此时\(m-n\)为最小的整数。答案是两种情况下的最小绝对值。代码:#include<bits/stdc++.h>usingnamespacestd;pair<......
  • 题解:CF2009D Satyam and Counting
    比较容易观察的一道题,但是场上不开longlong见祖宗了。由于这题的\(x\)最大值比较小,所以我们可以直接存每个坐标是否有点。有两种三角形符号条件:存在两个点\((x,0),(x,1)\),可以观察到任意的其它点都可以成为第三点。有三个点为\((x,0),(x+1,1),(x+2,0)\)或\((x,1),(x+1......