codeforce1442:C. Graph Transpositions(思维 + 最短路)

题目链接

在这里插入图片描述


题目大意:有一个边权为1的有向图,可以使用魔法将所有的边转向,第 i i i 次使用魔法要花费 2 i − 1 2^{i-1} 2i1 单位时间,求从 1 号点到 n n n 号点需要花费的最少时间,答案对 998244353 998244353 998244353 取模。


题解:魔法最多需要使用 n − 1 n - 1 n1 次,但当魔法使用超过18次时,由于 2 18 > 200000 2^{18} > 200000 218>200000,使用魔法次数更小的情况一定比使用魔法次数更多的情况更优。

对于魔法使用次数不超过18次的情况,答案也不会超过模数,令 d [ i ] [ j ] d[i][j] d[i][j] 表示从 1 1 1 i i i 使用了 j j j 次魔法的最短时间,可以暴力跑 d i j k s t r a dijkstra dijkstra 算法。

对于魔法使用次数超过18次的情况,答案会超过模数不能直接跑 d i j k s t r a dijkstra dijkstra,但我们只需要比较使用魔法的次数,在次数相同的情况下比较走过的边数。分别记录从1号到每个点走过的边数和使用的魔法次数,分奇偶进行转移。

注意:本题卡了 s p f a spfa spfa 算法

时间复杂度 O ( 20 n log ⁡ m ) O(20 n\log m) O(20nlogm)


代码:

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
const int mod = 998244353;
const int maxn = 2e5 + 10;
int n, m, res;
vector<pii> g[maxn];
int dp[maxn][22], vis[maxn][22];
int d[maxn][2], t[maxn][2];
int p[maxn];
struct ss {
	int u, i, v;
	ss () {}
	ss (int ui,int j,int vi) {
		u = ui;
		i = j;
		v = vi;
	}
	bool operator < (const ss &rhs) const {
		return v > rhs.v;
	}
};
struct node {
	int u, i, v, c;
	node() {}
	node(int ui,int j,int vi,int ci) {
		u = ui;
		i = j;
		v = vi;
		c = ci;
	}
	bool operator < (const node &rhs) const {
		if (c != rhs.c) return c > rhs.c;
		return v > rhs.v;
	}
};
void dijkstra1() {
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= 18; j++)
			dp[i][j] = mod, vis[i][j] = 0;
	priority_queue<ss> q;
	for (int i = 0; i <= 18; i++) {
		dp[1][i] = (1 << i) - 1;
		q.push(ss(1,i,dp[1][i]));
	}
	while (!q.empty()) {
		ss top = q.top(); q.pop();
		int u = top.u, i = top.i;
		if (vis[u][i]) continue;
		vis[u][i] = 1;
		for (auto it : g[u]) {
			int v = it.fir, c = it.sec;
			if (i % 2 != c) {
				if (i < 18 && dp[v][i + 1] > dp[u][i] + 1 + (1 << i)) {
					dp[v][i + 1] = dp[u][i] + 1 + (1 << i);
					q.push(ss(v,i + 1,dp[v][i + 1]));
				}
			} else {
				if (dp[v][i] > dp[u][i] + 1) {
					dp[v][i] = dp[u][i] + 1;
					q.push(ss(v,i,dp[v][i]));
				}
			}
		}
	}
}
void dijkstra2() {
	priority_queue<node> q;
	for (int i = 1; i <= n; i++) {
		d[i][0] = d[i][1] = mod;
		t[i][0] = mod; t[i][1] = mod;
		vis[i][0] = vis[i][1] = 0;
	}
	for (int i = 1; i <= n; i++) {
		if (dp[i][18] != mod) {
			d[i][0] = dp[i][18] - (1 << 18) + 1;
			t[i][0] = 18;
			q.push(node(i,0,d[i][0],t[i][0]));
		}
	}
	while (!q.empty()) {
		node top = q.top(); q.pop();
		int u = top.u, i = top.i;
		if (vis[u][i]) continue;
		vis[u][i] = 1;
		for (auto it : g[u]) {
			int v = it.fir, c = it.sec;
			if (i == c) {					//不改变奇偶 
				if (t[v][i] > t[u][i] || t[v][i] == t[u][i] && d[v][i] > d[u][i] + 1) {
					t[v][i] = t[u][i];
					d[v][i] = d[u][i] + 1;
					q.push(node(v,i,d[v][i],t[v][i]));
				}
			} else {
				if (t[v][i ^ 1] > t[u][i] + 1 || t[v][i ^ 1] == t[u][i] + 1 && d[v][i ^ 1] > d[u][i] + 1) {
					t[v][i ^ 1] = t[u][i] + 1;
					d[v][i ^ 1] = d[u][i] + 1;
					q.push(node(v,i ^ 1,d[v][i ^ 1],t[v][i ^ 1]));
				}
			}
		}
	}
}
int main() {
	scanf("%d%d",&n,&m);
	p[0] = 1;
	for (int i = 1; i <= 200000; i++)
		p[i] = p[i - 1] * 2 % mod;
	for (int i = 1; i <= m; i++) {
		int u, v; scanf("%d%d",&u,&v);
		g[u].push_back(pii(v,0));
		g[v].push_back(pii(u,1));
	}
	dijkstra1();
	dijkstra2();
	int res = mod;
	for (int i = 0; i <= 18; i++)
		res = min(res,dp[n][i]);
	if (res != mod) printf("%d\n",res);
	else {
		if (t[n][0] < t[n][1]) {
			printf("%d\n",(1ll * p[t[n][0]] - 1 + d[n][0] + mod) % mod);
		} else {
			printf("%d\n",(1ll * p[t[n][1]] - 1 + d[n][1] + mod) % mod);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值