CF1715E. Long Way Home -决策单调性、图

link:https://codeforces.com/contest/1715/problem/E

n 座城市,城市间有 m 条双向道路,通过第 i 条道路需要花费 wi 的时间,任意两个城市之间都有航班,乘坐城市 uv 之间的航班需要花费 (uv)2 的时间。
现在请对于任意城市 i(1in),求出从城市 1 出发,到达城市 i 所需要的最短时间,注意从城市 1i 的过程中最多乘坐 k 次航班
2n105 , 1m105 , 1k20


看到花费 =u2+v22uv 就感觉DNA动了,k20 也肯定是有用的,一个自然的想法就是做dp:f(i,j) 表示到 i 号点,使用了 j 次飞行的最小时间,那么 f(u,j) 可以是在 j1 轮次到达某个点 v ,再从 vu 花费 u2+v22uv 的代价转移,转移式子 f(u,j)=u2+minvf(v,j1)+v22uv 显然具有决策单调性,看成是若干条直线求上凸壳。
但也可以是在这一次之后又在图上走了一段,这部分就很麻烦了,可能需要枚举的点对很多,赛时在这里想了很久才意识到:为什么不每次转移完DP再跑一次dijkstra呢?dp求出了强制最后一下通过航班到达的最短时间,那就从 1 到每个点连上这样的边,然后再跑一次最短路就行。
(注:不能每次dp完都加上 O(n) 条边,这样就变成 O(nk2logn) 了…应该记录下额外加的这 n1 条边的编号,直接每次修改他们)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
typedef long long ll;
const int N=1e5+5;
constexpr ll INF=0x3f3f3f3f3f3f3f3f;
struct state{
int x;
ll w;
state(int x,ll w):x(x),w(w){}
bool operator <(const state &rhs)const{
return w>rhs.w;
}
};
int n,m,k;
vector<vector<pair<int,ll>>> G;
vector<ll> dijkstra(int st){
vector<ll> dis(n+1);
dis.assign(n+1,INF);
priority_queue<state> Q;
Q.emplace(st,dis[st]=0);
while(!Q.empty()){
auto [x,d]=Q.top();
Q.pop();
if(dis[x]!=d)continue;
for(auto [v,w]:G[x])if(dis[v]>dis[x]+w)
Q.emplace(v,dis[v]=dis[x]+w);
}
return dis;
}
struct Line{
ll k,b;
__int128 calc(int x){return (__int128)k*x+b;}
};
int main(){
fastio;
cin>>n>>m>>k;
G=vector<vector<pair<int,ll>>>(n+1);
rep(i,1,m){
int u,v,w;
cin>>u>>v>>w;
G[u].push_back({v,w});
G[v].push_back({u,w});
}
int st=G[1].size();
rep(i,2,n)G[1].push_back({i,INF});//i:G[1][st+i-2],
rep(tc,1,k){
auto d=dijkstra(1);
vector<ll> f(n+1);
vector<Line> S;
rep(i,1,n){
ll b3=(ll)i*i+d[i],k3=-2*i;
while(S.size()>=2){
int sz=S.size();
auto [k1,b1]=S[sz-2];
auto [k2,b2]=S[sz-1];
if((__int128)(b2-b1)*k1+(__int128)b1*(k1-k2)>(__int128)(b2-b1)*k3+(__int128)b3*(k1-k2))
S.pop_back();
else break;
}
S.push_back((Line){k3,b3});
}
int sz=S.size(),cur=0;
rep(i,1,n){
while(cur+1<=sz-1){
auto v1=S[cur].calc(i),v2=S[cur+1].calc(i);
if(v1>v2)cur++;
else break;
}
f[i]=(ll)i*i+S[cur].calc(i);
}
rep(i,2,n)G[1][st+i-2].second=min(G[1][st+i-2].second,f[i]);
}
auto d=dijkstra(1);
rep(i,1,n)cout<<d[i]<<' ';
return 0;
}
posted @   yoshinow2001  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示