「2017 山东一轮集训 Day6」重建 - 最短路 - dp

题目大意:
给一张无向带权连通图,和一些关键点,求一个最大的非负整数c,使得每条边的边权加上c之后,s到t的最短路等于s到t只经过关键点的最短路。
题解:观察到s到t的最短路是一个关于边数的分段函数,令f[x,i]表示从s出发走到x经过恰好i条边的最短路,g为经过关键点的数组。
那么分别意会出这两个分段函数后,就是求一个x,使得两个函数在此处取值相同。
注意到g[t,i]>=f[t,i],也就是当有某个位置x成为答案时,一定有某个i,设第i段的有效范围的交集是[L,R],则 x ∈ [ L , R ] x\in[L,R] x[L,R],并且此时g[t,i]=f[t,i]。
那么枚举这样的i,计算相应的R,然后对所有的R取个max即可。
注意i的范围不会超过初始时最短路上的边数,而这个不会超过n。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define db double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
namespace INPUT_SPACE{
	const int BS=(1<<24)+5;char Buffer[BS],*HD,*TL;
	char gc() { if(HD==TL) TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);return (HD==TL)?EOF:*HD++; }
	inline int inn()
	{
		int x,ch;while((ch=gc())<'0'||ch>'9');
		x=ch^'0';while((ch=gc())>='0'&&ch<='9')
			x=(x<<1)+(x<<3)+(ch^'0');return x;
	}
}using INPUT_SPACE::inn;
const int N=1010,M=10010;
const lint INF=LLONG_MAX/10;
struct edges{
	int to,pre,wgt;
}e[M<<1];int h[N],etop,imp[N];lint f[N][N],g[N][N];
inline int add_edge(int u,int v,int w) { return e[++etop].to=v,e[etop].wgt=w,e[etop].pre=h[u],h[u]=etop; }
struct node{
	pair<lint,int> d;int x;node(pii _d=mp(0,0),int _x=0) { d=_d,x=_x; }
	inline bool operator<(const node &n)const
	{ if(d.fir^n.d.fir) return d.fir>n.d.fir;return d.sec>n.d.sec; }
};pair<lint,int> d[N];int vis[N];priority_queue<node> q;
inline int dijkstra(int s,int t,int n)
{
	rep(i,1,n) d[i]=mp(INF,0),vis[i]=0;
	d[s]=mp(0,0),q.push(node(d[s],s));
	while(!q.empty())
	{
		int x=q.top().x;q.pop();
		if(vis[x]) continue;vis[x]=1;
		for(int i=h[x],y;i;i=e[i].pre)
		{
			pair<lint,int> nxt=mp(d[x].fir+e[i].wgt,d[x].sec+1);
			if(d[y=e[i].to]>nxt) d[y]=nxt,q.push(node(d[y],y));
		}
	}
	return d[t].sec;
}
inline int getdis(lint (*f)[N],int n,int d,int s)
{
	rep(i,1,n) rep(j,0,d) f[i][j]=INF;f[s][0]=0;
	rep(i,1,d) rep(x,1,n) if(imp[x]) for(int t=h[x],y;t;t=e[t].pre)
		if(imp[y=e[t].to]) f[x][i]=min(f[x][i],f[y][i-1]+e[t].wgt);
	return 0;
}
inline lint getinter(lint *f,int a,int b,int sqz)
{
	if(a>b) swap(a,b);
	if(f[a]<f[b]) return -1;
	if(f[a]==f[b]) return 0;
	if(f[a]==INF) return INF;
	lint x=f[a]-f[b],y=b-a;
	if(sqz) return (x-1)/y+1;
	return x/y;
}
inline int getrange(lint *f,int m,int p,lint &L,lint &R)
{
	L=-1,R=INF;
	rep(i,1,p-1) R=min(R,getinter(f,p,i,0));
	rep(i,p+1,m) L=max(L,getinter(f,p,i,1));
	return 0;
}
int main()
{
	for(int T=inn();T;T--)
	{
		int n=inn(),m=inn(),s=inn(),t=inn();lint ans=-1;
		memset(h,0,sizeof(int)*(n+1)),etop=0;int x,y,w;
		rep(i,1,m) x=inn(),y=inn(),w=inn(),add_edge(x,y,w),add_edge(y,x,w);
		int d=dijkstra(s,t,n);
		rep(i,1,n) imp[i]=1;getdis(f,n,d,s);
		rep(i,1,n) imp[i]=0;int k=inn();
		rep(i,1,k) imp[inn()]=1;getdis(g,n,d,s);
		rep(i,0,d) if(f[t][i]==g[t][i]&&f[t][i]<INF)
		{
			lint l,r;getrange(f[t],d,i,l,r);
			lint L,R;getrange(g[t],d,i,L,R);
			L=max(L,l),R=min(R,r);
			if(L<=R) ans=max(ans,R);
			if(ans==INF) break;
		}
		if(ans<0) printf("Impossible\n");
		else if(ans==INF) printf("Infinity\n");
		else printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值