其他分享
首页 > 其他分享> > [做题笔记] 那些未曾谋面的省选题

[做题笔记] 那些未曾谋面的省选题

作者:互联网

我的博客大概要封笔了,最后一周也不会做什么题了,再见了朋友们。

[HNOI2014] 道路堵塞

题目描述

点此看题

解法

我们不妨考虑增量法,先把在最短路径上的边排除掉,跑完最短路之后再慢慢添加边。

如果我们要求删除边 \(i\) 的答案,那么我们需要添加边 \([1,i)\),并且考虑 \((i,k]\) 边的影响(这些边我们是不加的),考虑把 \((i,k]\) 构成的路径染色,那么如果我们到达的某个点被染色,那么可以直接走最短路到终点

为了保证复杂度我们把给定的最短路径染色,如果现在 \(\tt spfa\) 更新到了最短路上的第 \(i\) 个点,那么我们这条路径打上时间戳 \(i\),如果删除的边 \(\in(i,k]\),那么这条拼凑出来的路径是对答案有贡献的,用一个堆维护即可。

时间复杂度基于 \(\tt spfa\),所以在不刻意卡的情况是可以通过的。

还有一种时间复杂度稳定的最短路树做法,找机会填坑。

总结

图论中的一些动态算法十分重要,巧用动态算法可以快速完成版本之间的转化,以解决一维偏序关系。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std; 
const int M = 200005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,tot,f[M],id[M],d[M],in[M];
queue<int> q;int rd[M],p[M],g[M],ban[M];
struct edge{int v,c,next;}e[M];
struct node
{
	int u,c;
	bool operator < (const node &b) const
		{return c>b.c;}
};priority_queue<node> s;
void spfa(int now)
{
	q.push(now);in[now]=1;
	while(!q.empty())
	{
		int u=q.front();q.pop();in[u]=0;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v,c=e[i].c;
			if(ban[i]) continue;
			if(d[v]>d[u]+c)
			{
				d[v]=d[u]+c;
				if(id[v]) s.push({id[v],d[v]+g[id[v]]}); 
				else if(!in[v]) in[v]=1,q.push(v);
			}
		}
	}
}
signed main()
{
	n=read();m=read();k=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),c=read();
		e[++tot]=edge{v,c,f[u]},f[u]=tot;
	}
	for(int i=1;i<=k;i++)
	{
		rd[i]=read();ban[rd[i]]=1;
		p[i+1]=e[rd[i]].v;id[p[i+1]]=i+1;
	}
	p[1]=1;id[1]=1;
	for(int i=k;i>=1;i--) g[i]=g[i+1]+e[rd[i]].c;
	memset(d,0x3f,sizeof d);
	d[1]=0;spfa(1);
	for(int i=1;i<=k;i++)
	{
		while(!s.empty() && s.top().u<=i) s.pop();
		if(s.empty()) puts("-1");
		else printf("%d\n",s.top().c);
		d[p[i+1]]=d[p[i]]+e[rd[i]].c;
		spfa(p[i+1]);
	}
}

标签:选题,const,谋面,int,路径,笔记,read,spfa,include
来源: https://www.cnblogs.com/C202044zxy/p/16063441.html