博艾市除了有海底高铁连接中国大陆、台湾与日本,市区里也有很成熟的轨道交通系统。我们可以认为博艾地铁系统是一个无向连通图。博艾有N个地铁站,同时有M小段地铁连接两个不同的站。
地铁计价方式很简单。从A站到B站,每经过一小段铁路(连接直接相邻的两个点的一条边),就要收取1博艾元。也就是说,从A站到B站,选择的路径不一样,要价也会不同。
我们认为凡华中学在1号地铁站。学生们通过地铁通勤,他们当然知道选择最短路来坐车的话,票价最便宜。
然而博艾地铁公司经营不善,一直亏损,于是他们打算提价。提价一次就是将一小段铁路原来收费1元改收2元。同一小段的铁路不会多次提价。他们打算提价Q次。
学生们知道,如果他们到学校的一条最短路径中的一小段提价了,可以改变路径,使总票价不变。然而随着一条一条的铁路被提价,当居住在某个站附近的学生发现,提价后,没有任何一种方案可以从家到学校的费用和初始费用相等时,就会不满。
现在地铁公司希望知道,对于每一次涨价,有多少个站,学生会因为涨价而不满呢?
第一行为三个整数N,M,Q。
接下来M行,每行2个整数ai,bi,表示第i条铁路连接的两个站。i表示铁路编号。
接下来Q行,每行一行整数rj,表示每次涨价的铁路编号。
输出格式:
Q行。每行一个整数表示不满的车站数量。
5 6 5 1 2 1 3 4 2 3 2 2 5 5 3 5 2 4 1 3
0 2 2 4 4
【样例解释】
次数 车站2 车站3 车站4 车站5
初始 1 1 2 2
1 1 1 2 2
2 1 2 2 3
3 1 2 2 3
4 2 2 3 3
5 2 2 4 3
【数据范围】
对于20%的数据 N≤100, Q≤30
对于40%的数据 Q≤30
对于70%的数据 正确的输出结果中,不会有超过50种不一样的整数(数据范围剧透解法系列)
对于100%的数据 N≤100000, Q≤M≤200000
题解:dfs+bfs
如果一条路径涨价了,那么为了达到花费最小的路径,那我们一定不会再走这条边了,所以涨价就等同于删边操作。
我们考虑倒序加边。一个车站会在何时不满呢?一定是他最后一条最短路径被破坏的时候,所以我们倒序加边,当加入一条边的时候,1到该点的路径第一次变成最短路径的长度,那么可知删掉这条边的时候就会不满+1.
我们可以利用bfs O(n)求出1到其他点的最短路径,记作minn[i]
设点x到1的当前路径长度为dis[x],若dis[x]==minn[x],则该点为扩展点
通过分析最短路性质发现,某个点v新成为扩展点情况有两种
(1)加边(u,v)更新,且dis[u]==d[u]&&dis[v]==d[u]+1&&d[v]!=dis[v]
(2)邻居u突然成为最终图最短路,且dis[v]==d[u]+1&&d[v]!=dis[v]
那么我们可以将没有涨价的边先加进去,bfs求出当前的dis。然后倒序加边,如果加边满足情况1,我们就dfs满足情况2的点,并统计该边增加的扩展点的个数。
因为每次点都之后被访问一遍,所以均摊时间复杂度是O(n)。
#include
#include
#include
#include
#include
#include
#define N 400003
using namespace std;
int n,m,q,tot;
int point[N],v[N],dis[N],next[N],num[N],minn[N];
int vis[N],pd[N],x[N],y[N],a[N],ans;
void add(int x,int y)
{
tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y;
tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void bfs()
{
queue p;
for (int i=1;i<=n;i++) dis[i]=0;
memset(vis,0,sizeof(vis));
dis[1]=0;
p.push(1); vis[1]=1;
while (!p.empty()){
int now=p.front(); p.pop();
for (int i=point[now];i;i=next[i])
{
if (vis[v[i]]) continue;
vis[v[i]]=1;
dis[v[i]]=dis[now]+1;
p.push(v[i]);
}
}
}
void dfs(int x)
{
ans++;
for (int i=point[x];i;i=next[i])
if (minn[v[i]]==dis[x]+1&&minn[v[i]]!=dis[v[i]]){
dis[v[i]]=dis[x]+1;
dfs(v[i]);
}
}
int main()
{
freopen("a.in","r",stdin);
//freopen("my.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
}
bfs();
for (int i=1;i<=n;i++) minn[i]=dis[i];
for (int i=1;i<=q;i++) scanf("%d",&a[i]),pd[a[i]]=1;
tot=0;
memset(point,0,sizeof(point));
memset(next,0,sizeof(next));
for (int i=1;i<=m;i++) if (!pd[i]) add(x[i],y[i]);
bfs();
for (int i=q;i>=1;i--) {
int t=a[i];
add(x[t],y[t]); int u=x[t]; int v1=y[t]; ans=0;
if (dis[u]==minn[u]&&minn[v1]==dis[u]+1&&minn[v1]!=dis[v1])
dis[v1]=dis[u]+1,dfs(v1);
if (dis[v1]==minn[v1]&&minn[u]==dis[v1]+1&&minn[u]!=dis[u])
dis[u]=dis[v1]+1,dfs(u);
num[i]=ans;
}
for (int i=1;i<=q;i++) num[i]+=num[i-1];
for (int i=1;i<=q;i++) printf("%d\n",num[i]);
}