Codeforces Gym100783H 最短路 其他

原文链接https://www.cnblogs.com/zhouzhendong/p/CF-Gym100783H.html

题目传送门 - CF-Gym100783H

题意

  给定一个 $n$ 个节点 $P$ 条带权边的无向图,有 $m$ 个特殊点。给定开始点 $X$ 和结束点 $Y$ 。

  现在请你求一个 $k$ ,使得令所有边的权值都加上 $k$ 之后,$X$~$Y$ 的最短路经过且仅经过特殊点,不能有其他的最短路途中任意一个节点不是特殊点。

  问满足条件的 $k$ 最大是多少。如果 $k=\infty$ ,则输出 $Infinity$ 。如果不存在这样的 $k$ 则输出 $Impossible$ 。否则输出 $k$ 的值。

  $n,m\leq 1000,\ \ \ \ \ \ P\leq 10000,\ \ \ \ \ 1\leq X,Y\leq n$

题解

  我们记原图为 $g1$,记仅包含特殊点和连接他们的边的图为 $g2$ 。

  对于 $g1$ 和 $g2$ 我们分别求出 $X$ 走 $i$ 步到 $Y$ 的最短路(按照原来的边权)。这个可以 nm simple dp 。

  我们记 $g1$ 和 $g2$ 中最多走 $i$ 步从 $S$ 到 $T$ 的最短路值分别为 $v1[i]$ 和 $v2[i]$ 。

  然后我们枚举一个 $i$ 并求走最多 $i$ 步到达 $T$ 在 $g_1$ 中的最短路值为全局最短路的 $k$ 的取值范围。

  这个取值范围如何求?

  我们得满足走 $i$ 步是最短的。所以:

  对于 $j=i$ : $v1[i]=v2[j]$

  对于 $j>i$ : $v1[i]+ki\leq v1[j]+kj\Longrightarrow v1[i]-v1[j]\leq k(j-i)\Longrightarrow k\geq \cfrac{v1[i]-v1[j]}{j-i}$

  对于 $j<i$ : $v1[i]+ki\leq v1[j]+kj\Longrightarrow v1[j]-v1[i]\geq k(i-j)\Longrightarrow k\leq \cfrac{v1[j]-v1[i]}{i-j}$

  如果满足上述条件的 $k$ 存在,那么我们可以用满足上述条件的最大 $k$ 值来更新答案。

  细心的你可能已经发现了,我这个做法忽略了可能存在的经过非特殊点的最短路的情况。

  要搞定这个东西其实很简单。我们取一个略大于 $n$ 的值 : $1024$,对于每一个初始边权 $v$,如果它连接的是两个特殊点,那么令 $v^\prime =1024v$ 否则 $v^\prime = 1024v+1$ 。

  这样有什么用呢?如果存在走 $j$ 步且经过非特殊点的最短路,那么 $dis \mod 1024 < j$ 。

  这个相等权值的情况要特别注意。别忘了在求之前 $k$ 的取值范围中也要加上关于这个的特判,微调一下 $k$ 的取值范围。

  建议提前判掉 $Infinity$ 的情况和部分 $Impossible$ 的情况。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1005;
const LL INF=1LL<<62;
int n,m,S,T;
int owned[N];
struct Gragh{
	static const int M=20005;
	int cnt,x[M],y[M],nxt[M],fst[N];
	LL z[M];
	void clear(){
		cnt=0;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b,int c){
		y[++cnt]=b,x[cnt]=a,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g,g2;
void Get_owned(){
	memset(owned,0,sizeof owned);
	int m,x;
	scanf("%d",&m);
	while (m--)
		scanf("%d",&x),owned[x]=1;
}
LL v1[N],v2[N];
LL dis[N][N];
void Getv(Gragh &g,LL v[]){
	for (int i=0;i<n;i++)
		for (int j=1;j<=n;j++)
			dis[i][j]=INF;
	dis[0][S]=0;
	for (int i=0;i<n-1;i++)
		for (int j=1;j<=g.cnt;j++)
			dis[i+1][g.y[j]]=min(dis[i+1][g.y[j]],dis[i][g.x[j]]+g.z[j]);
	for (int i=0;i<n;i++)
		v[i]=dis[i][T];
	for (int i=1;i<n;i++)
		v[i]=min(v[i-1],v[i]);
}
bool check1(){
	return v2[n-1]<INF;
}
bool check2(){
	int p1=0,p2=0;
	while (v1[p1]==INF)
		p1++;
	while (v2[p2]==INF)
		p2++;
	return p1==p2&&v1[p1]==v2[p2];
}
LL k1[N],k2[N];
LL solve3(){
	LL ans=-1,k=0;
	for (int i=n-1;i>0;i--){
		if (v1[i]!=v2[i]||v2[i]==INF)
			continue;
		LL Min=1,Max=INF;
		// v1[i]+k*i <= v1[j]+k*j
		// v1[i]-v1[j] <= k*(j-i)
		// k >= (v1[i]-v1[j]) / (j-i)
		for (int j=i+1;j<n;j++){
			LL now=((v1[i]>>10)-(v1[j]>>10)+j-i-1)/(j-i);
			if ((v1[j]&1023LL)!=j&&now*(j-i)==((v1[i]>>10)-(v1[j]>>10)))
				now++;
			Min=max(Min,now);
		}
		// v1[i]+k*i <= v1[j]+k*j
		// k <= (v1[j]-v1[i])/ (i-j)
		for (int j=0;j<i;j++){
			if (v1[j]==INF)
				continue;
			LL now=((v1[j]>>10)-(v1[i]>>10))/(i-j);
			if ((v1[j]&1023LL)!=j&&now*(i-j)==((v1[j]>>10)-(v1[i]>>10)))
				now--;
			Max=min(Max,now);
		}
		if (Min<=Max)
			ans=max(ans,Max);
	}
	return ans;
}
int main(){
while(~scanf("%d%d%d%d",&n,&m,&S,&T)){
	g.clear(),g2.clear();
	for (int i=1;i<=m;i++){
		int a,b,c;
		scanf("%d%d%d",&a,&b,&c);
		g.add(a,b,c);
		g.add(b,a,c);
	}
	Get_owned();
	for (int i=1;i<=m*2;i++){
		g.z[i]<<=10;
		if (owned[g.x[i]]&&owned[g.y[i]]){
			g.z[i]++;
			g2.add(g.x[i],g.y[i],g.z[i]);
		}
	}
	Getv(g,v1);
	Getv(g2,v2);
	if (!check1()){
		puts("Impossible");
		continue;
	}
	if (check2()){
		puts("Infinity");
		continue;
	}
	LL ans=solve3();
	if (ans<0)
		puts("Impossible");
	else
		printf("%lld\n",ans);
}
	return 0;
}

  

posted @ 2018-07-19 19:47  zzd233  阅读(226)  评论(0编辑  收藏  举报