【JZOJ5551】【20190625】旅途

题目

\(n\)个点\(m\)条边的无向图,一条路径的代价定义为路径上前\(k\)大边的边权和

对于$k = n \to 1 $,求1-n的最短路

\(n,m \le 3000 \ , \ w_i \le 10^9\)

题解

  • 60pts
  • 枚举最小的边\(e\)将边分为 $ \ge e $ 和 $ <e $ ,后者边权设为0,设\(dp_{i,j}\)表示走到\(i\)\(\ge e\)的边走了\(j\)条的概率
  • \(dp_{n,k}\)去更新答案,需要预先做一边最短路考虑边数不够的情况
  • 时间复杂度:\(O(m^2n\ log \ m)\)
  • 100pts
  • 枚举最小的\(e\),将每条边的边权设置为\(max\{0,w - w_e\}\) ,做Dij,用\(dis_{n} + k \times w_e\)更新答案
  • 时间复杂度:\(O(mn \log m)\)
#include<bits/stdc++.h>
#define ll long long
#define inf 1e18

using namespace std;

const int N=3010;
int T,n,m,o,hd[N],vis[N],del[N<<1],id[N<<1];
ll dis[N],ans[N];

struct Edge{int u,v,w,nt;}E[N<<1];
void adde(int u,int v,int w){
	E[o]=(Edge){u,v,w,hd[u]};hd[u]=o++;
	E[o]=(Edge){v,u,w,hd[v]};hd[v]=o++;
}

bool cmp(int x,int y){return E[x].w==E[y].w?x<y:E[x].w<E[y].w;}

struct data{
	int u;ll w;
	data(int _u=0):u(_u){w=dis[u];};
	bool operator <(const data&A)const{return w>A.w;};
};
priority_queue<data>q;

void dijkstra(int e){
	for(int i=1;i<=n;++i)dis[i]=inf;
	++T;dis[1]=0;q.push(1);
	while(!q.empty()){
		int u=q.top().u;q.pop();
		if(vis[u]==T)continue;
		vis[u]=T;
		for(int i=hd[u];i;i=E[i].nt){
			int v=E[i].v;
			if(del[i]){
				if(dis[v]>dis[u]){
					dis[v]=dis[u];
					if(vis[v]!=T)q.push(v);
				}
			}else{
				if(dis[v]>dis[u]+E[i].w-e){
					dis[v]=dis[u]+E[i].w-e;
					if(vis[v]!=T)q.push(v);
				}
			}
		}
	}
	for(int i=1;i<=n;++i)ans[i]=min(ans[i],dis[n]+1ll*e*i);
}

int main(){
	freopen("journey.in","r",stdin);
	freopen("journey.out","w",stdout);
	scanf("%d%d",&n,&m);o=1;
	for(int i=1;i<=m;++i){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		adde(u,v,w);
	}
	for(int i=1;i<o;++i)id[i]=i;
	sort(id+1,id+o,cmp);
	for(int i=1;i<=n;++i)ans[i]=inf;
	dijkstra(0);
	for(int i=1;i<o;++i){
		int now=id[i];
		if(del[now])continue;
		del[now]=del[now+1]=1;
		dijkstra(E[now].w);
	}
	for(int i=n;i;--i)printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2019-06-26 21:37  大米饼  阅读(207)  评论(0编辑  收藏  举报