洛谷 P1710 地铁涨价

来源:互联网 发布:c语言调用大漠插件 编辑:程序博客网 时间:2024/05/16 19:06

题目背景

本题开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;}
1 0
原创粉丝点击