发现本题最特别的一点:
K
K
K 的取值非常小
考虑按 K K K 划分阶段,在每个阶段内可以随便跑最短路,阶段间尝试转移 这个技巧貌似很熟悉
一种思路是分层图,但复杂度不行
考虑 DP : 设 f [ k ] [ i ] f[k][i] f[k][i] 表示坐了 k k k 次飞机, 1 1 1 到 i i i 的最小花费。转移显然: f [ k ] [ i ] = f [ k − 1 ] [ j ] + ( j − i ) 2 f[k][i]=f[k-1][j]+(j-i)^2 f[k][i]=f[k−1][j]+(j−i)2
每个阶段做完后再跑一边 最短路,即可拿到 30pts
考虑优化, d p dp dp 式子非常朴素,尝试斜率优化 j 2 + f [ k − 1 ] [ j ] = 2 i ⋅ j − i 2 + f [ k ] [ i ] j^2+f[k-1][j]=2i\cdot j-i^2+f[k][i] j2+f[k−1][j]=2i⋅j−i2+f[k][i]
本题不同的一点是,决策没有上下界限制
需要将 1 ∼ n 1\sim n 1∼n 所有决策加入队列后,再转移。显然满足所求直线斜率单增,维护队头即可
#include<bits/stdc++.h>
using namespace std ;
#define Y(j) (f[k-1][j]+1LL*j*j)
#define X(j) (j)
#define tt dq.size()-1
typedef long long LL ;
const int N = 1e5 + 100 ;
int n , m , K ;
struct nn
{
int lst , to ;
LL val ;
}E[2*N] ;
int head[N] , tot ;
inline void add( int x , int y , LL v )
{
E[++tot] = (nn){ head[x] , y , v } ;
head[x] = tot ;
}
struct nod
{
int id ; LL val ;
friend bool operator < ( nod x , nod y ) {
return x.val > y.val ;
}
};
priority_queue<nod> q ;
bool vis[N] ; LL dis[N] ;
LL f[21][N] ;
void dij( int k )
{
memset( vis , 0 , sizeof vis ) ;
while( !q.empty() ) {
int x = q.top().id ; q.pop() ;
if( vis[x] ) continue ;
vis[x] = 1 ;
for(int i = head[x] ; i ; i = E[i].lst ) {
int t = E[i].to ;
if( f[k][t] > f[k][x] + E[i].val ) {
f[k][t] = f[k][x] + E[i].val ;
q.push( (nod){t,f[k][t]} ) ;
}
}
}
}
deque<int> dq ;
int main()
{
scanf("%d%d%d" , &n , &m , &K ) ;
int x , y ; LL v ;
for(int i = 1 ; i <= m ; i ++ ) {
scanf("%d%d%lld" , &x , &y , &v ) ;
add( x , y , v ) , add( y , x , v ) ;
}
memset( f , 0x3f , sizeof f ) ;
f[0][1] = 0 ;
q.push( (nod){1,0} ) ;
dij(0) ;
for(int k = 1 ; k <= K ; k ++ ) {
dq.clear() ;
for(int i = 1 ; i <= n ; i ++ ) {
while( dq.size()>1 && (Y(dq[tt])-Y(dq[tt-1]))*(X(i)-X(dq[tt])) >= (Y(i)-Y(dq[tt]))*(X(dq[tt])-X(dq[tt-1])) ) dq.pop_back() ;
dq.push_back( i ) ;
}
for(int i = 1 ; i <= n ; i ++ ) {
int kk = 2*i ;
while( dq.size()>1 && (Y(dq[1])-Y(dq[0])) <= 1LL*kk*(X(dq[1])-X(dq[0])) ) dq.pop_front() ;
int j = dq.front() ;
f[k][i] = f[k-1][j] + 1LL*(j-i)*(j-i) ;
q.push((nod){i,f[k][i]}) ;
}
dij(k) ;
}
for(int i = 1 ; i <= n ; i ++ ) {
printf("%lld " , f[K][i] ) ;
}
return 0;
}
总结一下,本题重点还是在于 观察数据范围,将复杂的问题拆分成若干好处理的问题