把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

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

枚举第\(k\)大+最短路

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

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

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

  • 如果\(k'<k\),那么排名\(k'+1\sim k\)的边权都被视作\(v\)更新答案,肯定不优。
  • 如果\(k'>k\),那么排名\(k+1\sim 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\n",t),0;
}
posted @ 2021-06-13 09:37  TheLostWeak  阅读(64)  评论(0编辑  收藏  举报