CF1051F

技术文章 11个月前 完美者
2,042 0

标签:ext   路径   枚举   sizeof   lca   tin   char   efi   n+1   

题意:给出一个无向连通图,图中有\(n\)个点,\(m\)条边,**m\(-\)n$≤\(20**,给出\)q\(个询问,每一个询问包含两个整数\)u\(和\)v\(,对于每一次询问,输出\)u\(和\)v$之间的最短路

\(m-n\le20\) 说明图是很接近树的,但是这个图是树上有一些环

如果它是一棵树,维护一个前缀\(h\)\(h_v\)表示从根节点到\(v\)的距离,前缀和可以在一次\(dfs\)求出,对于树上两点距离,长度就是\(\large h_u+h_v-h_{lca(u,v)}\)

我们试着将\(m\)变大,\(m=n\)时,假设多的边连了\(x,y\),长度为\(z\),那么\(u,v\)间就会多了几条路,但是贡献的只有\(u\to x\to y\to v\)\(u\to y\to x\to v\)两条路,长度为\(dis(u,x)+z+dis(v,y)\)\(dis(v,x)+z+dis(v,y)\),与原来长度比较,三个求最小值

现在有\(m-n+1\)个非树边,这时,\(u,v\)间的路更多

考虑枚举这个路径经过的某一点\(i\),把这条路径拆成\(u\to i\)\(i\to v\)两部分,那么什么时候\(i\)对更新\(dis(u,v)\)有贡献呢

要跑一个最小生成树,这样就可以把\(n-1\)条树边搞出来

只需枚举\((m-n+1)\)条非树边的最多\(2(m-n+1)\)个端点,\(dis(u,v)\)这条路,只有两种情况

1.没经过非树边,只经过树边,这种情况就是\(dis(u,v)\)初始值

2.经过某条非树边,这条路径一定是经过这条非树边两端点

所以\(dis(u,v)\)可以通过\(dis(u,i)+dis(i,v)\)更新,因为是无向图,式子变成了\(dis(i,u)+dis(i,v)\),所以跑\(i\)点开始的单源最短路,又因为\(m-n\le20\),所以\(i\)最多\(42\)个,所以最多跑\(42\)遍最短路即可

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
#define int long long
const int N = 2e5 + 25;
inline int read(){
	int x = 0;char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x;
}
int n,m,Q;
int dep[N],fa[N][21],happen[N],vis[N];
ll dis[50][N],Tdis[N];
vector<int>p;
struct edge{ll u,v,w,f,next;}e[N<<1]; int head[N],tot;
inline void add(int u,int v,int z){
	e[tot] = (edge){u,v,z,0,head[u]}; head[u] = tot++;
}
void dfs(int x, int _fa) {
    vis[x] = 1; dep[x] = dep[_fa] + 1;
    for(int i = head[x]; ~i; i = e[i].next) {
        int to = e[i].v;
        if(vis[to]) continue;
        e[i].f = e[i ^ 1].f = 1;
        Tdis[to] = Tdis[x] + (ll)e[i].w;
        fa[to][0] = x;
        dfs(to, x);
    }
}
void Pre() {
    for(int j = 1; j <= 20; j++)
        for(int i = 1; i <= n; i++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
void dijkstra(int x, int id) {
    memset(dis[id], 0x7f, sizeof(dis[id])); dis[id][x] = 0;
    memset(vis, 0, sizeof(vis));
    priority_queue<pair<int,int> > q; q.push(make_pair(0, x));
    while(!q.empty()) {
        int p = q.top().second; q.pop();
        if(vis[p]) continue;
        for(int i = head[p]; ~i; i = e[i].next) {
            int to = e[i].v;
            if(dis[id][to] > dis[id][p] + e[i].w && (!vis[to]))
                dis[id][to] = dis[id][p] + e[i].w, q.push(make_pair(-dis[id][to], to));
        }
    }
}
inline int lca(int x, int y) {
    if(dep[x] < dep[y]) swap(x, y);
    for(int i = 20; i >= 0; --i)
        if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
    if(x == y) return x;
    for(int i = 20; i >= 0; --i)
        if(fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}
signed main(){
	memset(head,-1,sizeof(head));
	n = read(); m = read();
    for(int i = 1; i <= m; i++) {
        int x = read(), y = read(), z = read();
        add(x, y, z);
        add(y, x, z);
    }
    dfs(1, 0);
    for(int i = 0; i < tot; i++)
        if(!e[i].f) {
            if(!happen[e[i].u]) p.push_back(e[i].u), happen[e[i].u] = 1;
            if(!happen[e[i].v]) p.push_back(e[i].v), happen[e[i].v] = 1;
        }

    for(int i = 0; i < p.size(); i++)
        dijkstra(p[i], i);
    Pre();
    int Q = read();
    while(Q--) {
        int x = read(), y = read();
        ll ans = Tdis[x] + Tdis[y] - 2 * Tdis[lca(x, y)];
        for(int i = 0; i < p.size(); i++)
            ans = min(ans, dis[i][x] + dis[i][y]);
        cout << ans << endl;
    }
}

CF1051F

标签:ext   路径   枚举   sizeof   lca   tin   char   efi   n+1   

原文地址:https://www.cnblogs.com/shikeyu/p/13904304.html

版权声明:完美者 发表于 2020-10-31 2:24:02。
转载请注明:CF1051F | 完美导航

暂无评论

暂无评论...