[NOI2014]魔法森林(动态加边+SPFA)

【题解】

求两个变量构成的最优值,可以考虑限制一个变量,最优化另一个 
观察此题,可以得到这样一个思路:
假设已知答案中的Ai的最大值不超过x,只需最小化1到n路径上的Bi的最大值 
不难想到二分这个Ai的上限x
更新方式为SPFA,即,设d[i]为:只考虑Ai<=x的边,从1到i的路径上BiMax的最小值 
然而这样的话,对于每个x,d数组都要重新求 
不如我们按Ai从小到大加边,随着加边来更新 BiMax 的最小值,这样d[i]只需在原有的基础上更新即可,不必全部重新求 
每加入一条边,就将它的两个端点加入队列,用它们来更新其他点的d值 
最后,更新答案的方式为:if(ans>ta[i]+d[n]) ans=ta[i]+d[n]; 因为若d[n]变得更优了,一定是因为走了新加入的边的缘故,因此AiMax必定是新加入变的A值 

复杂度不会分析,一个数据结构题就这么被水过去了,不知我现在的水平,要是在考场上,能不能机智地拿上这分 //flag不敢立


在B站的时间、代码量排名中似乎比较优秀?


【代码】

#include<stdio.h>
#include<stdlib.h>
#define INF 1000000
int tu[100005],tv[100005],tb[100005],ta[100005];
int v[200005],A[200005],B[200005],first[50005],next[200005],d[50005],hash[50005],q[10000005];
int e=0;
int max(int a,int b)
{
	if(a>b) return a;
	return b;
}
void jh(int* a,int* b)
{
	int t=*a;
	*a=*b;
	*b=t;
}
void tj(int x,int y,int ta,int tb)
{
	v[++e]=y;
	A[e]=ta;
	B[e]=tb;
	next[e]=first[x];
	first[x]=e;
}
void kp(int low,int high)
{
	int i=low,j=high,mid=ta[(i+j)/2];
	while(i<j)
	{
		while(ta[i]<mid) i++;
		while(ta[j]>mid) j--;
		if(i<=j)
		{
			jh(&tu[i],&tu[j]);
			jh(&tv[i],&tv[j]);
			jh(&ta[i],&ta[j]);
			jh(&tb[i],&tb[j]);
			i++;
			j--;
		}
	}
	if(j>low) kp(low,j);
	if(i<high) kp(i,high);
}
int main()
{
	int n,m,i,j,head,tail,ans=INF;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;i++)
		scanf("%d%d%d%d",&tu[i],&tv[i],&ta[i],&tb[i]);
	kp(1,m);
	for(i=2;i<=n;i++)
		d[i]=INF;
	for(i=1;i<=m;i++)
	{
		tj(tu[i],tv[i],ta[i],tb[i]);
		tj(tv[i],tu[i],ta[i],tb[i]);
		head=0;
		tail=2;
		q[0]=tu[i];
		q[1]=tv[i];
		hash[tu[i]]=hash[tv[i]]=i;
		while(head<tail)
		{
			for(j=first[q[head]];j!=0;j=next[j])
				if( d[v[j]] > max(d[q[head]],B[j]) )
				{
					d[v[j]] = max(d[q[head]],B[j]);
					if(hash[v[j]]!=i)
					{
						q[tail++]=v[j];
						hash[v[j]]=i;
					}
				}
			hash[q[head++]]=0;
		}
		if(ans>ta[i]+d[n]) ans=ta[i]+d[n];
	}
	if(ans!=INF) printf("%d",ans);
	else printf("-1");
	return 0;
}


你可能感兴趣的:(限制,预处理,SPFA,NOI)