[CF1442 C] Graph Transpositions

[题目链接]

https://codeforces.com/contest/1442/problem/C

[题解]

首先考虑最朴素的做法。

\(f_{i , j}\) 表示现在在 \(i\) , 经过 \(j\) 次翻转操作的最小值。 这是个简单的分层图最短路问题。

问题在于 \(j\) 的范围是 \(O(N)\) 级别的。

但考虑每次翻转的花费 \(2 ^ {K}\)\(K \geq 18\) 时就会超过 \(N\) , 不妨先对所有 \(j \leq 18\) 做一遍最短路。

对于 \(K \leq 18\) 的情况。显然对于每一层 , 多走 \(1\) 是不劣的。 因此直接枚举层数进行最短路即可。

时间复杂度 : \(O(NK)\)

[代码]


#include <bits/stdc++.h>
 
using namespace std;
 
typedef long long LL;
 
const int MN = 2e5 + 5;
const int INF = 2e9;
const int mod = 998244353;
 
typedef pair < int , int > pii;
#define mp make_pair
 
int N , M , res;
vector < int > E[2][MN];
int dp[MN][19] , inq[MN][19] , dis[MN] , two[MN];
queue < pii > q;
 
inline void AddEdge(int u , int v , int type) {
	E[type][u].push_back(v);
	return;
}
inline void chkmin(int &x , int y) {
	x = min(x , y);
}
inline void sssp() {
	for (int i = 1; i <= N; ++i) 
	for (int j = 0; j <= 18; ++j)
		dp[i][j] = INF;
	dp[1][0] = 0;
	q.push(mp(1 , 0));
	while (!q.empty()) {
		int u = q.front().first , k = q.front().second;
		q.pop(); inq[u][k] = false;
	 	for (auto v : E[k & 1][u]) {
	 		 if (dp[u][k] + 1 < dp[v][k]) {
	 		 	 dp[v][k] = dp[u][k] + 1;
				 if (!inq[v][k]) {
				 	 inq[v][k] = true;
				 	 q.push(mp(v , k));
				 } 
			 } 
		 }
		 if (k < 18 && dp[u][k] + (1 << k) < dp[u][k + 1]) {
		 	 dp[u][k + 1] = dp[u][k] + (1 << k);
		 	 if (!inq[u][k + 1]) {
			  	 inq[u][k + 1] = true;
				 q.push(mp(u , k + 1));
			 }
		 }
	} 
	for (int i = 0; i <= 18; ++i) chkmin(res , dp[N][i]);
}
 
int main() {
	
	scanf("%d%d" , &N , &M);
	for (int i = 1; i <= M; ++i) {
		int u , v; scanf("%d%d" , &u , &v);
		AddEdge(u , v , 0) , AddEdge(v , u , 1);
	}
	res = INF; sssp();
	if (res < INF) {
		printf("%d\n" , res);
		return 0;
	}
	for (int i = 1; i <= N; ++i) dis[i] = INF; 
	two[0] = 1;
	for (int i = 1; i <= N; ++i) two[i] = 2ll * two[i - 1] % mod;
	queue < int > q , qq;
	q.push(1); dis[1] = 0; qq.push(1);
	for (int k = 0; ; ++k) {
		while (!q.empty()) {
			int u = q.front(); q.pop();
			if (u == N) {
				printf("%d\n" , 1LL * ((dis[u] + two[k] - 1) % mod + mod) % mod);
				return 0;
			}
			for (int v : E[k & 1][u]) 
				if (dis[u] + 1 < dis[v]) {
					dis[v] = dis[u] + 1;
					q.push(v); qq.push(v);	
				}
		}	
		while (!qq.empty()) {
			q.push(qq.front());
			qq.pop();
		}
	}
	printf("%d\n" , dis[N]);
	return 0;
}
posted @ 2020-11-22 19:11  evenbao  阅读(202)  评论(0编辑  收藏  举报