题目链接:点击查看
题目大意:给出一个由 n 个点和 m 条边组成的有向图,现在有一个人,他有一条固定的路线,这个题目中有一个导航,当到达任意一个点时,导航都会给出一条通往终点的最短路,但是这个人固定的路线并不一定每次都是最短路,导航最初会给出一条最短路,如果这个人不按照最短路行走的话,那么导航需要“重构”最短路,题目问“重构”的最小次数和最大次数
题目分析:其实读完题后,首先第一反应是先将所有的边置反,然后对于终点求一次迪杰斯特拉,因为这是CF,怕hack,所以尽量还是别用spfa吧,然后题目给出的固定路线中有 k 个点,即 k - 1 条边,对于每条边判断,无非只有三种情况,我们记ans0为最小次数,ans1为最大次数:设这条边为 u -> v
- 当前边不是最短路上的边,那么无论如何走,从点 u 到点 v 后,导航一定会重构一次最短路,那么ans0++,ans1++
- 当前边是最短路上的边,显然ans0不变,因为选择当前路就可以使得最短路最短,且无需重构
- 如果当前边是最短路的必经边,也就是从点 u 到终点只有 u ->v 这一条最短路满足路程最短,ans1不变,因为没有其他选择了
- 如果当前边不是最短路的必经边,那么必然存在另一条边 u -> t 满足点 u 到终点的距离仍然是最短路,此时选择 u -> t 这条路可以使ans1++
然后直接模拟上述三种情况就好了,为了方便书写,迪杰斯特拉我用了链式前向星的模板,建立反向边,而正向边仍然用邻接表储存,用于最后统计答案时用
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;//顶点数
const int M=2e5+100;//边数
struct Edge
{
int to,w,next;
}edge[M];
int head[N],d[N],cnt;//链式前向星
bool vis[N];
void addedge(int u,int v,int w)
{
edge[cnt].to=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
struct Node
{
int to,w;
Node(int TO,int W)
{
to=TO;
w=W;
}
bool operator<(const Node& a)const
{
return w>a.w;
}
};
void Dijkstra(int st)
{
priority_queue<Node>q;
memset(vis,false,sizeof(vis));
memset(d,inf,sizeof(d));
d[st]=0;
q.push(Node(st,0));
while(q.size())
{
Node cur=q.top();
int u=cur.to;
q.pop();
if(vis[u])
continue;
vis[u]=true;
for(int i=head[u];i!=-1;i=edge[i].next)//扫描出所有边
{
int v=edge[i].to;
int w=edge[i].w;
if(d[v]>d[u]+w)//更新
{
d[v]=d[u]+w;
q.push(Node(v,d[v]));
}
}
}
}
void init()
{
memset(head,-1,sizeof(head));
cnt=0;
}
vector<int>node[N];
int a[N];
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
init();
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
node[u].push_back(v);
addedge(v,u,1);
}
int k;
scanf("%d",&k);
for(int i=1;i<=k;i++)
scanf("%d",a+i);
Dijkstra(a[k]);
int ans_0=0,ans_1=0;
int u=a[1];
int dis=d[u];
for(int i=2;i<=k;i++)
{
int v=a[i];
if(d[u]!=d[v]+1)//新加的边不在最短路上
{
ans_1++,ans_0++;
}
else//在最短路上
{
for(auto vv:node[u])
{
if(vv==v)
continue;
if(d[u]==d[vv]+1)
{
ans_1++;
break;
}
}
}
u=v;
}
printf("%d %d\n",ans_0,ans_1);
}