题目背景
本题开O2优化,请注意常数
题目描述
博艾市除了有海底高铁连接中国大陆、台湾与日本,市区里也有很成熟的轨道交通系统。我们可以认为博艾地铁系统是一个无向连通图。博艾有N个地铁站,同时有M小段地铁连接两个不同的站。
地铁计价方式很简单。从A站到B站,每经过一小段铁路(连接直接相邻的两个点的一条边),就要收取1博艾元。也就是说,从A站到B站,选择的路径不一样,要价也会不同。
我们认为凡华中学在1号地铁站。学生们通过地铁通勤,他们当然知道选择最短路来坐车的话,票价最便宜。
然而博艾地铁公司经营不善,一直亏损,于是他们打算提价。提价一次就是将一小段铁路原来收费1元改收2元。同一小段的铁路不会多次提价。他们打算提价Q次。
学生们知道,如果他们到学校的一条最短路径中的一小段提价了,可以改变路径,使总票价不变。然而随着一条一条的铁路被提价,当居住在某个站附近的学生发现,提价后,没有任何一种方案可以从家到学校的费用和初始费用相等时,就会不满。
现在地铁公司希望知道,对于每一次涨价,有多少个站,学生会因为涨价而不满呢?
输入输出格式
输入格式:
第一行为三个整数N,M,Q。
接下来M行,每行2个整数ai,bi,表示第i条铁路连接的两个站。i表示铁路编号。
接下来Q行,每行一行整数rj,表示每次涨价的铁路编号。
输出格式:
Q行。每行一个整数表示不满的车站数量。
输入输出样例
输入样例#1:
5 6 5
1 2
1 3
4 2
3 2
2 5
5 3
5
2
4
1
3
输出样例#1:
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
【分析】
不得不感叹这真是一道好题…
转化问题
这里路线涨价明显等同于删边,所以我们可以把问题倒过来思考:
图上依次(倒序)加边,问每个点成为最终图最短路的时间
分析
记原图的点1到达点i的最短路为dis[i],当前状态下点1到达点i最短路为d[i]。下面称d[i]==dis[i]的点i为扩展点。
通过分析最短路性质发现,某个点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]
(其实上面是同一种情况XD)
重要的是,每个点只会被更新1次,体现在了上面的强调处。这是很显然的,因为这题答案是唯一确定的,但这个是降低复杂度的重要条件。
确定算法
1.首先把最终图的最短路情况dis[i]求出来。然后重建图,去掉所有待加边。
2.然后依次加入待加边,检测边的两端是否符合条件1,若符合,则进行深度优先搜索,对新成为扩展点邻居进行条件2判断。
3.将每次新成为扩展点的数目记录,最后处理出答案。
复杂度分析
1.由于边权为1,求dis[]可以用bfs遍历,复杂度为O(n+m)。
2.加入了q条边,在整个过程2中每个点只会被dfs到一次,所以复杂度为O(n+m+q)。
3.每一次的答案是新成为扩展点的数目,所以需要一遍前缀和,复杂度为O(q)。
最终复杂度为O(n+m+q)
数组开小…查了一个小时qwqqq
【代码】
//洛谷月赛 3.地铁涨价
#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxn=400005;
queue <int> que;
int n,m,q,cnt,res;
int head[mxn],dis[mxn],ans[mxn],del[2*mxn],d[mxn];
struct node {int to,next;}f[mxn*4];
bool vis[mxn*4],b[mxn];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void add(int u,int v)
{
f[++cnt].to=v;
f[cnt].next=head[u];
head[u]=cnt;
}
inline void dfs(int u,int fa)
{
for(register int i=head[u];i;i=f[i].next)
if(!vis[i])
{
int v=f[i].to;
if(v==fa) continue;
if(dis[v]!=d[v] && d[v]==d[u]+1 && dis[u]==d[u])
res++,dis[v]=d[v],dfs(v,u);
}
}
inline void spfa(bool ha)
{
int i,j,num=0;
M(b);
memset(dis,0x3f,sizeof dis);
while(!que.empty()) que.pop();
dis[1]=0;b[1]=1;
que.push(1);
while(!que.empty())
{
int u=que.front();
que.pop();
for(i=head[u];i;i=f[i].next)
if(!vis[i])
{
int v=f[i].to;
if(!b[v])
{
dis[v]=dis[u]+1;
que.push(v),b[v]=1;
}
}
}
if(ha) fo(i,1,n) d[i]=dis[i]; //d储存未删边的图的状态,dis储存当前状态
}
int main()
{
int i,j,u,v,w;
n=read(),m=read(),q=read();
fo(i,1,m)
{
u=read(),v=read();
add(u,v);add(v,u);
}
spfa(1);
fo(i,1,q)
{
w=read();
del[i]=w;
vis[2*w]=vis[2*w-1]=1;
}
spfa(0);
for(i=q;i;i--)
{
res=0;
w=del[i]*2;
vis[w]=vis[w-1]=0;
u=f[w].to,v=f[w-1].to;
if(d[u]==dis[u] && d[v]==dis[u]+1 && dis[v]!=d[v]) //如果u能扩展至v
res++,dis[v]=d[v],dfs(v,u);
swap(u,v);
if(d[u]==dis[u] && d[v]==dis[u]+1 && dis[v]!=d[v]) //如果u能扩展至v
res++,dis[v]=d[v],dfs(v,u);
ans[i]=res;
}
fo(i,1,q)
ans[i]+=ans[i-1],printf("%d\n",ans[i]);
return 0;
}