P r o b l e m \mathrm{Problem} Problem
小P和小R在玩一款益智游戏。游戏在一个正权有向图上进行。
小P控制的角色要从
A
A
A点走最短路到
B
B
B点,小R控制的角色要从
C
C
C点走最短路到
D
D
D点。
一个玩家每回合可以有两种选择,移动到一个相邻节点或者休息一回合。
假如在某一时刻,小P和小R在相同的节点上,那么可以得到一次特殊奖励,但是在每个节点上最多只能得到一次。
求小P和小R最多能获得多少次特殊奖励。
S o l u t i o n \mathrm{Solution} Solution
有一个比较显然的结论就是:
- 两者的共同路径一定是连续的。
- 假设存在两段不连续的路径,则取两者之间的公共端点一定可以使答案更优。
那么我们就要找到两者最短路图的交集,求该交集DAG的最长链。
一些细节:
- 可以两条路径之间只有公共点没有公共边,所以需要特殊判断公共点并将f[公共点]=1.
- 最后输出的答案不要边的最长链+1,因为最终的答案有可能出现答案为0的现象。
C o d e \mathrm{Code} Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e5;
int n, m, A, B, C, D, res(0);
int dis[6][N], vis[N], f[N], in[N];
vector < int > a[N];
vector < pair<int,int> > G1[N], G2[N];
struct node {
int x, y, v;
} e[N];
int read(void)
{
int s = 0, w = 0; char c = getchar();
while (c < '0' || c > '9') w |= c == '-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
void Dijkstra(int k, vector < pair<int,int> > a[N], int S)
{
memset(vis,0,sizeof vis);
memset(dis[k],30,sizeof dis[k]);
priority_queue < pair<int,int> > q;
dis[k][S] = 0; q.push({0,S});
while (q.size())
{
int x = q.top().second; q.pop();
if (vis[x]) continue; vis[x] = 1;
for (int i=0;i<a[x].size();++i)
{
int y = a[x][i].first;
int v = a[x][i].second;
if (dis[k][x] + v < dis[k][y]) {
dis[k][y] = dis[k][x] + v;
q.push({-dis[k][y],y});
}
}
}
return;
}
int Topsort(void)
{
queue < int > q;
for (int i=1;i<=n;++i)
if (in[i] == 0) q.push(i);
while (q.size())
{
int x = q.front(); q.pop();
for (int i=0;i<a[x].size();++i)
{
int y = a[x][i];
in[y] --;
f[y] = max(f[y],f[x]+1);
res = max(res,f[y]);
if (in[y] == 0) q.push(y);
}
}
return res;
}
signed main(void)
{
n = read(), m = read();
for (int i=1,x,y,v;i<=m;++i)
{
x = read(), y = read(), v = read();
G1[x].push_back({y,v});
G2[y].push_back({x,v});
e[i] = node{x,y,v};
}
A = read(), B = read();
C = read(), D = read();
Dijkstra(1,G1,A);
Dijkstra(2,G2,B);
Dijkstra(3,G1,C);
Dijkstra(4,G2,D);
if (dis[1][B] > 1e12 || dis[3][D] > 1e12)
return puts("-1"), 0;
for (int i=1;i<=n;++i)
if (dis[1][i] + dis[2][i] == dis[1][B]
&& dis[3][i] + dis[4][i] == dis[3][D])
res = f[i] = 1;
if (res == 0) return puts("0"), 0;
for (int i=1,x,y,v;i<=m;++i)
{
int tag = 0;
x = e[i].x, y = e[i].y, v = e[i].v;
if (dis[1][x] + dis[2][y] + v == dis[1][B]
&& dis[3][x] + dis[4][y] + v == dis[3][D]) tag = 1;
if (tag) a[x].push_back(y), in[y] ++;
}
cout << Topsort() << endl;
return 0;
}