P4042 [AHOI2014/JSOI2014]騎士遊戲 SPFA瞎搞||DIJKSTRA+堆
阿新 • • 發佈:2020-09-11
題面:
現有N個怪物,每個怪物有兩個屬性\(S_i,K_i\),表示普通攻擊和法術攻擊殺死該怪物的消耗,同時普通攻擊殺死該怪物會產生新的\(R_i\)個怪物,初始只有1號怪物,求使得場上沒有怪物的消耗最小
範圍&性質:$ 2\le n\le 2\times10^5,1\le \sum R_i\le 10^6,1\le K_i,s_i\le 5\times 10^{14}$
分析:
很容易想到將怪物前後產生關係作為建邊的方案,但是因為普通攻擊產生的後置影響無法直接計算,所以要在最短路的基礎上瞎搞 /bushi
可以推得轉移式如下:
\[dis[u]=min(dis[u],dis[v]+s[i]+\sum dis[r[u][i]]) \]
然後我們發現ta很像SPFA的鬆弛操作,但是對於每一個節點無法保證更新它時他的所有兒子的\(dis[v]\)都已經被更新了,所以每次鬆弛後要將父親節點再次入隊,等待下一次更新
程式碼:
#include<bits/stdc++.h> using namespace std; namespace zzc { const int maxn = 1e6+5; long long n; long long dis[maxn],s[maxn]; struct edge { int to,nxt; }e[maxn],f[maxn]; int cnt=0,head[maxn]; void add(int u,int v) { e[++cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt; } int fcnt=0,fhead[maxn]; void fadd(int u,int v) { f[++fcnt].to =v; f[fcnt].nxt=fhead[u]; fhead[u]=fcnt; } bool ins[maxn]; void spfa() { queue<int> q; for(int i=1;i<=n;i++) q.push(i); while(!q.empty()) { int u=q.front();q.pop(); ins[u]=false; long long tmp=s[u]; for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; tmp+=dis[v]; } if(dis[u]>tmp) { dis[u]=tmp; for(int i=fhead[u];i;i=f[i].nxt) { int fa=f[i].to; if(ins[fa]) continue; q.push(fa); ins[fa]=true; } } } } void work() { scanf("%lld",&n); for(int i=1;i<=n;i++) { long long tmp,x; scanf("%lld%lld%lld",&s[i],&dis[i],&tmp); while(tmp--) { scanf("%lld",&x); add(i,x); fadd(x,i); } } spfa(); printf("%lld\n",dis[1]); } } int main() { zzc::work(); return 0; }