【題解】Luogu P5122 Fine Dining G
阿新 • • 發佈:2021-11-20
\(Dijkstra\) 水一發
對於題目,我們可以轉化為如下圖所示:
我們設點 \(s\) 到點 \(e\) 的距離就是題目裡面每個點到終點的距離,然後節點 \(t\) 就是多出的乾草(有可能在最短路上,有可能在最短路外)。設點到點的距離為 \(dis\) ,乾草垛的美味度為 \(k\) 值,那麼題目就是求:
移項,可得:
\[dis_{s->t}+dis_{t->e}\geq k+dis_{s->e} \]毫無疑問,右式極易求出,那麼問題就變成求左式了。
如果分佈求解的話,兩個不確定的點 \(s\)
那再轉化一下:
我們映像出一個超級點,只會連結點 \(t\) 並且長度與點 \(t\) 到終點的長度一樣。
這樣我們再以超級點為起點做最短路的時候,是一定會經過點 \(t\) 的。
問題再次轉化: \[dis_{s->e'}\geq k+dis_{s->e} \]
但是在一開始的時候我們就預設 \(dis\) 值是最大值,所以我們最好將大於等於轉化為小於等於。
轉化一下:
自己理解一下就可以了。
#include<iostream> #include<cstdio> #include<queue> #include<cstring> #define Maxn 500000 using namespace std; typedef long long ll; const ll INF=0x7f7f7f7f7f; ll n,m,x,k,u,v,w,hay[Maxn]; struct Edge{ int to,next,val; }edge[Maxn<<1]; ll top,head[Maxn]; void add(ll u,ll v,ll w){ edge[++top].to=v; edge[top].next=head[u]; edge[top].val=w; head[u]=top; } bool vis[Maxn]; ll dis[Maxn]; struct Node{ ll dis,pos; Node(int dis,int pos):dis(dis),pos(pos){} bool operator <(const Node &x) const{ return dis>x.dis; } }; priority_queue<Node>q; void Dijkstra(int s){ for(ll i=1;i<=n;i++) dis[i]=INF; dis[s]=0; q.push(Node(0,s)); while(!q.empty()){ Node temp=q.top(); q.pop(); ll u=temp.pos; ll d=temp.dis; if(vis[u]) continue; vis[u]=true; for(ll i=head[u];i;i=edge[i].next){ ll v=edge[i].to; if(dis[v]>dis[u]+edge[i].val){ dis[v]=dis[u]+edge[i].val; if(!vis[v]) q.push((Node){dis[v],v}); } } } } ll ans1[Maxn]; int main(){ scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=m;i++){ scanf("%lld%lld%lld",&u,&v,&w); add(u,v,w),add(v,u,w); } Dijkstra(n); for(int i=1;i<=n;i++) ans1[i]=dis[i]; for(int i=1;i<=k;i++){ scanf("%lld%lld",&x,&hay[i]); add(x,n+1,ans1[x]-hay[i]),add(n+1,x,ans1[x]-hay[i]); } memset(vis,false,sizeof(vis)); Dijkstra(n+1); for(int i=1;i<n;i++){ if(dis[i]<=ans1[i]) printf("1\n"); else printf("0\n"); } return 0; }