你说得对,但是我没调出来(有注释)
查看原帖
你说得对,但是我没调出来(有注释)
678858
ShiRoZeTsuHL卜奎BBQ!楼主2023/12/26 18:58

自己口胡的写法()

为啥会有零零星星的错误啊,完全不知道怎么回事

抓狂·

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;

const int maxn = 1e4 + 5;
const int maxm = 2e4 + 5;

namespace G {
  //原图
	struct edge { int to, w, nxt; } e[maxm<<1];
	int tot = 1, head[maxn];
	inline void add(int u, int v, int w) {
		e[++tot].to = v;
		e[tot].w = w;
		e[tot].nxt = head[u];
		head[u] = tot;
	}
}

namespace g {
  //圆方树
	struct edge { int to, w, nxt; } e[maxn<<1];
	int tot = 1, head[maxn<<1];
	inline void add(int u, int v, int w) {
		e[++tot].to = v;
		e[tot].w = w;
		e[tot].nxt = head[u];
		head[u] = tot;
	}
}

int n, m, q, dfncnt, cnt, top;
int dfn[maxn], low[maxn], stk[maxn];
int ring[maxn], val[maxn];
int dis[maxn<<1], dep[maxn<<1], st[14][maxn<<1];
bool vis[maxn];
vector<int> pv[maxn];

inline void build(int u, int last) {
  //建圆方树
	using namespace G;
	dfn[u] = low[u] = ++dfncnt;
	stk[++top] = u;
	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if(i == last) continue;
		if(!dfn[v]) {
			build(v, i^1);
			low[u] = min(low[u], low[v]);
			if(low[v] >= dfn[u]) {
              //把这个点双的点都存进vector里
				pv[++cnt].push_back(u);
				int x;
				while((x = stk[top--])) {
					pv[cnt].push_back(x);
					if(x == v) break;
				}
			}
		}
		else low[u] = min(low[u], dfn[v]);
	}
}

inline void bfs(int id) {
  //bfs求出第 id 个环上,每个点距离最前面那个点的距离
  //vis[u] 表示 u 是否在当前的点双上
  //ring[id] 代表整个环的长度
	using namespace G;
	queue<int> q;
	q.push(pv[id][0]);
	dis[pv[id][0]] = 0;
	while(!q.empty()) {
		int u = q.front(); q.pop();
		vis[u] = false;
		for(int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if(!vis[v]) continue;
			ring[id] += e[i].w;
			val[v] = dis[v] = dis[u] + e[i].w;
			q.push(v);
		}
	}
}

inline void dfs(int u, int fa) {
  //dfs预处理 lca,还有每个点到根节点的距离
	using namespace g;
	st[0][u] = fa;
	dep[u] = dep[fa] + 1;
	for(int i = 1; (1<<i)+1 <= dep[u]; i++)
		st[i][u] = st[i-1][st[i-1][u]];

	for(int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
	    dis[v] = dis[u] + e[i].w;
		dfs(v, u);
	}
}

inline int lca(int& x, int& y) {
  //求lca的同时,也能求出距离 x, y 最近的 lca的儿子
	if(dep[x] < dep[y]) swap(x, y);
	for(int i = 13; i >= 0; i--)
		if(dep[y]+(1<<i) <= dep[x]) x = st[i][x];
	if(x == y) return x;
	for(int i = 13; i >= 0; i--)
		if((1<<i)<dep[x] && st[i][x] != st[i][y])
			x = st[i][x], y = st[i][y];
	return st[0][x];
}

int main() {
	freopen("P5236_1.in", "r", stdin);
	freopen("test.out", "w", stdout);
	
	scanf("%d %d %d", &n, &m, &q);
  //原图加边
	for(int i = 1, u, v, w; i <= m; i++) {
		using namespace G;
		scanf("%d %d %d", &u, &v, &w);
		add(u, v, w); add(v, u, w);
	}
    build(1, 0);

    for(int i = 1; i <= cnt; i++) {
      //把每个点双都处理一下
		using namespace g;
		for(int x : pv[i]) vis[x] = true;
		bfs(i);
      //只有两个点,说明只是一条割边,直接连接即可
		if(pv[i].size() == 2) add(pv[i][0], pv[i][1], ring[i]);
		else {
		    add(pv[i][0], i+n, 0);
            for(int x : pv[i]) if(x != pv[i][0]) add(i+n, x, min(dis[x], ring[i]-dis[x]));
		}
	}
	dfs(1, 0);

	for(int i = 1, x, y; i <= q; i++) {
		scanf("%d %d", &x, &y);
		int ans = dis[x] + dis[y], zx = lca(x, y);
		if(zx <= n) printf("%d\n", ans - (dis[zx]<<1));
      //lca 是圆点,直接求
		else {
			int tmp = min(abs(val[x]-val[y]), ring[zx-n] - abs(val[x]-val[y]));
			printf("%d\n", ans + tmp - (dis[x]+dis[y]));
          //否则就求距离x, y 最近的,lca的两个儿子之间的最短距离
		}
	}
	return 0;
}
2023/12/26 18:58
加载中...