[最短路] [斜率优化] 模拟赛好题分享

在这里插入图片描述
发现本题最特别的一点: 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[k1][j]+(ji)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[k1][j]=2iji2+f[k][i]

本题不同的一点是,决策没有上下界限制

需要将 1 ∼ n 1\sim n 1n 所有决策加入队列后,再转移。显然满足所求直线斜率单增,维护队头即可

#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;
}

总结一下,本题重点还是在于 观察数据范围,将复杂的问题拆分成若干好处理的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值