• [CF1051F]The Shortest Statement


    题目大意:给出一张$n$个点$m(m-nleqslant20)$条边的无向图,$q$询问两点之间的最短路。$n,m,q⩽10^5$

    题解:询问$10^5$肯定不能每次求最短路,发现$m-nleqslant20$,也就是说这张图是一棵树加上最多$21$一条非树边,这$21$条非树边最多连接$42$个不同的点。答案要么是在树上的答案($dep_u+dep_v-2dep_{LCA(u,v)}$),要么就经过了至少一条非树边。

    至少经过一条非树边比较难处理。可以对每个连接了非树边的点跑一遍最短路,因为这是唯一导致答案变小的方案。

    卡点:求$LCA$时$x,y$写反,导致减乘负数($Codeforces$评测机好评,每次$RE$都可以帮我找到哪出锅了)

    C++ Code:

    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define maxn 100010
    #define maxm 100030
    #define maxk 45
    const long long inf = 0x3f3f3f3f3f3f3f3f;
    inline long long min(long long a, long long b) {return a < b ? a : b;}
    
    int head[maxn], cnt = 1;
    struct Edge {
    	int to, nxt;
    	long long w;
    	bool tree;
    } e[maxm << 1];
    inline void addE(int a, int b, long long c) {
    	e[++cnt] = (Edge) {b, head[a], c, false}; head[a] = cnt;
    }
    int n, m;
    namespace Tree {
    	#define M 18
    	int fa[maxn][M + 1], dep[maxn];
    	long long sum[maxn];
    	void dfs(int u) {
    		for (int i = head[u]; i; i = e[i].nxt) {
    			int v = e[i].to;
    			if (dep[v]) continue;
    			fa[v][0] = u; dep[v] = dep[u] + 1;
    			sum[v] = sum[u] + e[i].w;
    			e[i].tree = e[i ^ 1].tree = true;
    			dfs(v);
    		}
    	}
    	inline void init(int rt) {
    		dep[rt] = 1;
    		dfs(rt);
    		for (int i = 1; i <= M; i++) 
    			for (int j = 1; j <= n; j++) 
    				fa[j][i] = fa[fa[j][i - 1]][i - 1];
    	}
    	inline int LCA(int x, int y) {
    		if (x == y) return x;
    		if (dep[x] < dep[y]) std::swap(x, y);
    		for (int i = dep[x] - dep[y]; i; i &= i - 1) x = fa[x][__builtin_ctz(i)];
    		if (x == y) return x;
    		for (int i = M; ~i; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    		return fa[x][0];
    	}
    	#undef M
    }
    using Tree::LCA;
    using Tree::sum;
    
    int S[maxk], top;
    bool vis[maxn];
    long long d[maxk][maxn];
    struct node {
    	int p;
    	long long d;
    	node () {p = d = 0;}
    	node (int __p, long long __d = 0) {p = __p, d = __d;}
    	inline bool operator < (const node &rhs) const {return d > rhs.d;}
    };
    std::priority_queue<node> q;
    void dij(int S, long long *d) {
    	while (!q.empty()) q.pop();
    	for (int i = 1; i <= n; i++) d[i] = inf, vis[i] = false;
    	d[S] = 0;
    	q.push(node(S));
    	while (!q.empty()) {
    		int u = q.top().p; q.pop();
    		if (vis[u]) continue;
    		vis[u] = true;
    		for (int i = head[u]; i; i = e[i].nxt) {
    			int v = e[i].to;
    			if (d[v] > d[u] + e[i].w) {
    				d[v] = d[u] + e[i].w;
    				q.push(node(v, d[v]));
    			}
    		}
    	}
    }
    int main() {
    	scanf("%d%d", &n, &m);
    	for (int i = 0, a, b, c; i < m; i++) {
    		scanf("%d%d%d", &a, &b, &c);
    		addE(a, b, c);
    		addE(b, a, c);
    	}
    	Tree::init(1);
    	for (int i = 2; i <= cnt; i += 2) {
    		if (!e[i].tree) {
    			int u = e[i ^ 1].to, v = e[i].to;
    			if (!vis[u]) S[++top] = u, vis[u] = true;
    			if (!vis[v]) S[++top] = v, vis[v] = true;
    		}
    	}
    	for (int i = 1; i <= top; i++) dij(S[i], d[i]);
    	int QQ;
    	scanf("%d", &QQ);
    	while (QQ --> 0) {
    		int u, v;
    		scanf("%d%d", &u, &v);
    		long long ans = sum[u] + sum[v] - sum[LCA(u, v)] * 2;
    		for (int i = 1; i <= top; i++) {
    			ans = min(ans, d[i][u] + d[i][v]);
    		}
    		printf("%lld
    ", ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    C#仿QQ设置界面导航
    C#Winform之等待窗体
    《DevExpress》记录之TreeList
    winform窗体取消最大化双击标题最大化
    微信链接非80端口问题解决方案(伪处理)
    《微信企业号开发日志》之接收普通消息
    《微信企业号开发日志》本地调试程序四
    Jmeter取数据库数据进行参数传递
    洛谷 P1969 积木大赛
    洛谷 P1414 又是毕业季II(未完成)
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/9744320.html
Copyright © 2020-2023  润新知