【洛谷 P1710】地铁涨价(dfs+bfs)

24 篇文章 0 订阅
13 篇文章 0 订阅



【题解】【BFS+dfs】

【图论题】

【通过分析题目,我们可得知改变边权实际就是在删边。那么,如果每次删一条边,那么就要重新跑最短路,这样必然会T】

【考虑离线处理,改删边位加边,看每次加一条边,有多少段最短路径会改变。】

【先求一个初始状态下的最短路径,然后,再把要删的边全部删去,再求一次最短路。然后开始加边,每加入一条边,就判断这条边是不是初始状态下的最短路径,如果是,那么,再从这条边的两个端点再向外扩展,看还影响几段路径。加边(u,v)更新,且dis[u]==d[u]&&dis[v]==d[u]+1&&d[v]!=dis[v]

【最后求一个前缀和,输出】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{
	int s,t,num;
}road[200010];
int a[800010],nxt[800010],p[100010],tot;
int dis[100010],dft[100010],ans[200010];
int n,m,q,nm[200010],cnt;
bool vis[100010],b[200010];
inline void add(int x,int y)
{
	tot++; a[tot]=y; nxt[tot]=p[x]; p[x]=tot;
	tot++; a[tot]=x; nxt[tot]=p[y]; p[y]=tot;
}
inline void bfs(int s,int dis[])
{
	memset(vis,0,sizeof(vis));
	queue<int>que;
	dis[s]=0; vis[s]=1; que.push(s);
	while(!que.empty())
	 {
	 	int u=que.front(); que.pop();
	 	for(int i=p[u];i!=-1;i=nxt[i])
	 	 if(dis[a[i]]>dis[u]+1)
	 	  {
	 	  	dis[a[i]]=dis[u]+1;
	 	  	if(!vis[a[i]]) vis[a[i]]=1,que.push(a[i]);
		   }
	 }
}
void dfs(int x,int fa)
{
	for(int i=p[x];i!=-1;i=nxt[i])
	 if(a[i]!=fa&&dis[a[i]]==dft[x]+1&&dis[a[i]]!=dft[a[i]]) 
	  {
	  	dft[a[i]]=dft[x]+1; cnt++;
	  	dfs(a[i],x);
	  }
}
int main()
{
	freopen("int.txt","r",stdin);
	int i,j;
	memset(b,1,sizeof(b));
	memset(p,-1,sizeof(p));
	memset(nxt,-1,sizeof(nxt));
	memset(dis,127/3,sizeof(dis));
	memset(dft,127/3,sizeof(dft));
	scanf("%d%d%d",&n,&m,&q);
	for(i=1;i<=m;++i)
	 {
	 	int x,y;
	 	scanf("%d%d",&x,&y);
	 	road[i].s=x; road[i].t=y; road[i].num=i;
	 	add(x,y);
	 }
	bfs(1,dis); tot=0;
	memset(p,-1,sizeof(p));
	memset(nxt,-1,sizeof(nxt));
	for(i=1;i<=q;++i) scanf("%d",&nm[i]),b[nm[i]]=0;
	for(i=1;i<=m;++i)
     if(b[i]) add(road[i].s,road[i].t);
    bfs(1,dft);
    for(i=q;i>0;--i)
     {
     	int x=road[nm[i]].s,y=road[nm[i]].t;cnt=0;
     	if(dis[x]==dft[x]&&dis[y]==dft[x]+1&&dft[y]!=dis[y]) cnt++,dft[y]=dft[x]+1,dfs(y,x);
     	if(dis[y]==dft[y]&&dis[x]==dft[y]+1&&dft[x]!=dis[x]) cnt++,dft[x]=dft[y]+1,dfs(x,y);
     	ans[i]=cnt; add(x,y);
	 }
	for(i=2;i<=q;++i) ans[i]+=ans[i-1];
	for(i=1;i<=q;++i) printf("%d\n",ans[i]);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值