Loading

P4042 [AHOI2014/JSOI2014] 骑士游戏 - 图论,堆

题解

都 2021 年了,不会还真有人用 SPFA 做最短路题吧?

\(f_i\) 为消灭 \(i\) 需要的最小花费,则 \(f_i=\min(s_i,k_i+\sum_{(i,j)\in G} f_j)\)。这个转移有环,但考虑一个性质:\(s_i\) 最小的点一定不会被其他点更新。于是我们可以用这个点更新其它点的答案,然后从其它点中取出 \(f\) 最小的一个,进行相同的操作。

这其实就是类似 Dijkstra 的过程,时间复杂度 \(\mathcal{O}((n+\sum r_i)\log n)\)

还有一种 \(\mathcal{O}(n\log n+\sum r_i)\) 的做法,就是开始时将所有点插入堆,对于每个没被取出的点,直到它被所有连向它的点都松弛了一遍的时候才再次将它入堆。所以每个点最多入堆两次。

代码(法一)

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T>
void Read(T &_x){
	_x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
	_x*=_f;
}
template<typename T,typename... Args>
void Read(T &_x,Args& ...others){
	Read(_x);Read(others...);
}
typedef long long ll;
typedef pair<ll,int> pli;
typedef __gnu_pbds::priority_queue<pli,greater<pli>,__gnu_pbds::pairing_heap_tag> Heap;
const int N=2e5+5;
int n;ll s[N],k[N],f[N];
vector<int> g[N],revg[N];
Heap::point_iterator it[N];
int main(){
	Read(n);
	Heap q;
	For(i,1,n){
		int len;
		Read(s[i],k[i],len);
		For(j,1,len){
			int x;Read(x);
			g[x].push_back(i),revg[i].push_back(x);
		}
	}
	For(i,1,n){
		f[i]=s[i];
		for(int u:revg[i]) f[i]+=k[u];
		it[i]=q.push({min(k[i],f[i]),i});
	}
	while(!q.empty()){
		int u;ll w;
		tie(w,u)=q.top();
		q.pop();
		for(int v:g[u]){
			f[v]-=k[u]-w;
			if(k[u]>w&&f[v]<k[v]) q.modify(it[v],{f[v],v});
		}
	}
	printf("%lld\n",f[1]);
	return 0;
}
posted @ 2021-10-27 23:07  Alan_Zhao_2007  阅读(35)  评论(0编辑  收藏  举报