1. 程式人生 > 其它 >[做題筆記] 那些未曾謀面的省選題

[做題筆記] 那些未曾謀面的省選題

我的部落格大概要封筆了,最後一週也不會做什麼題了,再見了朋友們。

[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]);
	}
}