题目
题意大概是:给一个有向图。每次可以执行两个操作中的任意一个:
1、 沿着边走一步 花费1s
2、 把图翻转,第一次翻转花费0秒第二次翻花费2秒第三次花费4秒……
问从1 到 n花费的最短时间。
题解
他翻转不了几次,然后就不翻转了。
但是这要怎么写呢?
一、
次数少的话可以建分层图, 很简单。
20层以后肯定是能不翻转就不翻转。
所以先建20层图,跑个最短路。如果能走到那就是最短路了。
二、
但是有那种翻转的少走不过去的样例。怎么办?
这种情况因为要取模,不能直接用距离表示。况且建不出来图。。
建两层图,一层是正的一层是反的。层与层之间边权为一个特殊值(我用的-1)距离用两个值表示:翻转的次数和走的距离。
肯定是翻转越少越好。所以优先队列先按翻转次数排。再按走的距离排。
求最短路的过程中如果遇到-1 代表翻转次数+1。
代码:
#include<stdio.h>
#include <algorithm>
#include <vector>
#include <string>
#include <math.h>
#include <queue>
#include <string.h>
#include <iostream>
// #include <unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<int,char> pic;
#define pb push_back
#define mkp make_pair
#define st first
#define sd second
// const ll INF = 0x3f3f3f3f3f3f3f;
const double esp = 1e-9;
const int inf = 0x3f3f3f3f;
const int INF = 0x3f3f3f3f;
//!!!!!!!!!!!!!!!!!!!!!!!!
const int mod = 998244353;
const int maxn = 2e5 * 21;
struct Edge
{
int id;
ll val;
int nex;
}edge[maxn << 1];
int cnt =0 ;
int head[maxn];
void add(int x,int y,ll val)
{
edge[cnt].id = y;
edge[cnt].val = val;
edge[cnt].nex = head[x];
head[x] = cnt ++ ;
}
int vis[maxn];
int dis[maxn];
struct cmp
{
bool operator() (const pii& a, const pii& b)
{
return a.sd > b.sd;
}
};
priority_queue<pii,std::vector<pii>,cmp> qq;
int n;
void dj1()
{
qq.push(mkp(1,0));
dis[1] = 0;
while(!qq.empty())
{
int x = qq.top().st;
qq.pop();
if(vis[x])
continue;
vis[x] = 1;
for(int i = head[x]; ~i; i = edge[i].nex)
{
int v = edge[i].id;
if(vis[v] == 0 && dis[v] > dis[x] + edge[i].val)
{
dis[v] = dis[x] + edge[i].val;
qq.push(mkp(v,dis[v])); // 脑瘫 写成了vis[v] 一直wa
}
}
}
}
pii e[maxn];
struct Node
{
int id, c, dis;
Node(int ii,int cc,int dd):id(ii), c(cc), dis(dd){};
Node(){};
bool operator < (const Node& a) const {
if(c == a.c)
return dis > a.dis;
return c > a.c;
}
};
priority_queue<Node> qt;
pii d[maxn];
void dj2()
{
qt.push(Node(1,0,0));
d[1] = mkp(0,0);
while(!qt.empty())
{
int x = qt.top().id;
qt.pop();
if(vis[x])
continue;
vis[x] = 1;
for (int i = head[x]; ~i; i = edge[i].nex)
{
int v = edge[i].id;
int c = 0;
int dis = 0;
if(edge[i].val == -1)
{
dis = d[x].sd;
c = d[x].st + 1;
}
else
{
dis = d[x].sd + 1;
c = d[x].st;
}
if(vis[v] == 0 && d[v] > mkp(c, dis))
{
d[v] = mkp(c,dis);
qt.push(Node(v,c,dis));
}
}
}
}
int main()
{
int m;
scanf("%d%d",&n,&m);
for (int i = 1; i <= 20 * n; i ++ )
{
head[i] = -1;
}
for (int i = 1; i <= m; i ++ )
{
int x, y;
scanf("%d%d",&x,&y);
e[i] = mkp(x,y);
}
int step = 19;
for (int i = 0; i <= step; i ++ )
{
for (int j = 1; j <= m; j ++ )
{
int x = e[j].st + i * n;
int y = e[j].sd + i * n;
if(i & 1)
{
// printf("%d %d\n",y,x);
add(y,x,1);
}
else
{
// printf("%d %d\n",x,y);
add(x,y,1);
}
}
if(i != step)
{
for (int j = 1; j <= n; j ++ )
{
int x = i * n + j;
int y = (i + 1) * n + j;
// printf("%d %d %d\n",x,y,1 << i);
add(x,y,1 << i);
}
}
}
/*
init
*/
for (int i = 1; i <= 20 * n; i ++ )
{
dis[i] = INF;
vis[i] = 0;
}
dj1();
int ans = INF;
for (int i= 0; i <= step ;i ++ )
{
int x = (i + 1) * n;
// printf("%d %lld 1111111\n",x,dis[x]);
ans = min(ans,dis[x]);
}
// printf("%lld\n",ans);
if(ans != INF)
{
printf("%d\n",ans);
return 0;
}
cnt = 0;
for (int i = 1; i <= 2 * n; i ++ )
{
vis[i] = 0;
d[i] = mkp(inf,inf);
head[i] = -1;
}
for (int i = 1; i<= m; i ++ )
{
int x = e[i].st;
int y = e[i].sd;
add(x, y, 1);
add(y + n, x + n, 1);
}
for (int i = 1; i<= n; i ++ )
{
add(i,i + n, -1);
add(i + n, i, -1);
}
dj2();
pii t = mkp(inf,inf);
for (int i = 0; i <= 1; i ++ )
{
int x = (i + 1) * n;
t = min(t,d[x]);
}
// printf("%d %lld\n",t.st,t.sd);
ll s= 1;
ll sum = 0;
for (int i= 1; i <= t.st; i ++ )
{
sum = sum + s;
sum %= mod;
s <<= 1;
s %= mod;
}
sum = (sum + t.sd) % mod;
printf("%lld\n",sum);
}