【洛谷6961】[NEERC2017] Journey from Petersburg to Moscow(三分+最短路)

点此看题面

  • 给定一张(n)个点(m)条边的无向图,定义一条路径的长度为其中前(k)大的边权之和。
  • 求从(1)(n)的最短路。
  • (n,mle3000)

枚举第(k)大+最短路

考虑枚举第(k)大的权值(v),然后将所有边权减去(v)(原本小于(v)的设为(0))。

接着只需很普通地跑遍最短路,加上(k imes v)更新答案即可。

关于正确性,对于一条路径,设这个边权是第(k')大的:

  • 如果(k'<k),那么排名(k'+1sim k)的边权都被视作(v)更新答案,肯定不优。
  • 如果(k'>k),那么排名(k+1sim k')的边权(w)也都对答案产生了(w-v)的额外贡献,肯定不优。

因此每条路径确实只会在枚举到它的第(k)大权值时取到最优答案,正确性得证。

三分优化

实际上这个第(k)大权值(v)是可以三分的,这样一来就能做到更优的复杂度。

代码:(O(nlognlogm))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 3000
#define LL long long
#define add(x,y,z) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y,e[ee].v=z)
using namespace std;
int n,m,k,ee,lnk[N+5],ev[N+5];struct edge {int to,nxt,v;}e[2*N+5];
typedef pair<LL,int> Pr;priority_queue<Pr,vector<Pr>,greater<Pr> > q;
int vis[N+5];LL dis[N+5];I LL Dij(CI v)//最短路
{
	RI i,k;for(i=1;i<=n;++i) dis[i]=1e18,vis[i]=0;q.push(make_pair(dis[1]=0,1));
	W(!q.empty()) if(k=q.top().second,q.pop(),!vis[k]) for(vis[k]=1,i=lnk[k];i;i=e[i].nxt)
		dis[k]+max(e[i].v-v,0)<dis[e[i].to]&&(q.push(make_pair(dis[e[i].to]=dis[k]+max(e[i].v-v,0),e[i].to)),0);//边权减v
	return dis[n];
}
int main()
{
	RI i,x,y,z;for(scanf("%d%d%d",&n,&m,&k),i=1;i<=m;++i) scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z),ev[i]=z;
	RI l=0,r,m1,m2;sort(ev+1,ev+m+1),r=unique(ev+1,ev+m+1)-ev-1;//排序去重
	#define Calc(i) (Dij(ev[i])+1LL*k*ev[i])//最短路+k*第k大
	W(r-l>2) m1=l+(r-l)/3,m2=m1+(r-l)/3,Calc(m1)<Calc(m2)?r=m2:l=m1;//三分答案
	LL t=1e18;for(i=l;i<=r;++i) t=min(t,Calc(i));return printf("%lld
",t),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6961.html