CF1648E

题目传送门

题意简述

在一带权无向图上,每次只能连续经过两条边,且代价为两条边边权之和的平方。求单源最短路。

数据范围 \(1\leq n\leq10^5,\ 1\leq m \leq 2\times10^5,\ 1\leq w\leq50\)

分析

暴力建图的话总边数为 \(m^2\),会爆炸。

考虑一个路径 \(u\to x\to v\)\(w_1=w_{u,x},\ w_2=w_{x,v}\)

由于一次移动至少要经过两条边,所以上述代价和等价于将边权全部移到 \(x\to v\) 上。

如图。

这样的话 \(u\to x\) 就没那么重要了。在 \(w_2\) 一定的情况下共有 \(w=50\) 种可能的权值。

可以考虑以 \(w_1\) 的值构造分层图。

\((w,u)\) 为入边边权为 \(w\) 且入点为 \(u\)点集。但是由于其并不影响结果,可以将其作为一个点。

则对于点对 \((u,x,v)\),可以将 \(u\)\((w_1,u)\) 连一条边权为 \(0\) 的边;将 \((w_1,u)\)\(v\) 连一条边权为 \((w_1+w_2)^2\) 的边。

显然 \(u\to v\) 的代价等价于原图。

举个例子。

分层前:

分层后:边权什么的可以自己脑补

显然,对于一个点 \(u\)(假定其为路径的起始点),分层后其最大出度为 \(w=50\)

总边数从 \(m^2\) 骤降到了 \(mw\)

总时间复杂度为 \(O(m\log m)\) 左右。

class Solution {
	struct Graph {
		struct edge {
			int u, v, w, next;
		};
		int head[maxn], idx;
		edge e[maxm];
		void add(int x, int y, int z) {
			idx++; e[idx].u = x, e[idx].v = y, e[idx].w = z;
			e[idx].next = head[x], head[x] = idx;
		}
	} G, revG; 
	struct point {
		int u; ll dis;
		point(int a, ll b) {u = a, dis = b;}
		friend bool operator < (point a, point b) {
			return a.dis > b.dis;
		}
	};
	int n, m;
	std::vector <int> route;
	ll ans[maxn];
	ll dis[maxn];
	bool mk[maxn];
	ll in[maxn], out[maxn];
	void dijkstra(int st, Graph &G) {
		std::priority_queue <point> q;
		for(int i = 1; i <= n; i++) dis[i] = inf, mk[i] = 0;
		dis[st] = 0, q.push(point(st, 0));
		while(!q.empty()) {
			int u = q.top().u; q.pop();
			if(mk[u]) continue;
			mk[u] = 1; route.push_back(u);
			for(int j = G.head[u]; j; j = G.e[j].next) {
				int v = G.e[j].v, w = G.e[j].w;
				if(dis[v] > dis[u] + w) dis[v] = dis[u] + w, q.push(point(v, dis[v]));
			}
		}
	}
	void Count(int st) {
		dijkstra(st, G);
		for(int i = 1; i <= n; i++) in[i] = 0, out[i] = 0;
		in[st] = 1;
		for(auto v : route) {
			for(int i = revG.head[v]; i; i = revG.e[i].next) {
				int u = revG.e[i].v, w = revG.e[i].w;
				if(dis[v] == dis[u] + w) in[v] += in[u], in[v] %= mod;
			}
		}
		std::reverse(route.begin(), route.end());
		for(auto u : route) {
			out[u] = 1;
			for(int i = G.head[u]; i; i = G.e[i].next) {
				int v = G.e[i].v, w = G.e[i].w;
				if(dis[v] == dis[u] + w) out[u] += out[v], out[u] %= mod;
			}
		}
		route.clear();
		for(int i = 1; i <= m; i++) {
			int u = G.e[i].u, v = G.e[i].v, w = G.e[i].w;
			if(dis[v] == dis[u] + w) ans[i] += std::max(1ll, in[u]) * std::max(1ll, out[v]), ans[i] %= mod;
		}
	}
	public: void solve() {
		scanf("%d %d", &n, &m);
		for(int i = 1; i <= m; i++) {
			int u, v, w;
			scanf("%d %d %d", &u, &v, &w);
			G.add(u, v, w), revG.add(v, u, w);
		}
		for(int i = 1; i <= n; i++) Count(i);
		for(int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
	}
}
posted @   CQWDX  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示