1. 程式人生 > >魔法森林[NOI2014]

魔法森林[NOI2014]

解決 生成 是否 出錯 生成樹 div sin 最值 clu

時間限制: 2 Sec 內存限制: 512 MB

技術分享

技術分享

技術分享

技術分享

題解

對於NOI的題已經產生了一種崇敬……因為多半是很鍛煉思維能力的題,想出來很困難,實現的過程卻樂在其中。這道題大概可以看做最短路問題,但是麻煩之處在於有兩個參數,而且要求的最值是兩參數之和。據說正解是LCT?並沒有學過,等將來有一天能學到的話再來補一發正解吧~

有一種有理有據、非常可行的方法可以解決掉這道題。把它看做一個類似於生成樹的問題來解決(就像前兩天的考試題《tarvel》一樣逐步建圖),先把邊按照a的大小排序,每一次都把權值相等的邊一起加入鄰接表中,把邊的兩個端點放入隊列。然後跑spfa,求到終點的路上b最大值的最小值(並沒有什麽不對2333),每一次終點的dis被更新都是因為當前新加入的邊的緣故,所以比較剛剛加進去的邊的權值+dis[n]與結果的大小,這樣就能非常巧妙地解決問題了。

實現的時候又因為一點邊界問題卡了半天。如果每一次都按照這條邊和上一條邊是否相等來確定同一權值的邊是否已經被加完的話,最後加入的一組邊就被忽略了。這種小問題總是出錯,必須要更細心些。

技術分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int sj=100010;
queue<int> q;
int n,m,h[sj>>1
],e,dis[sj>>1],ans,tp; bool r[sj>>1]; struct YB { int xi,yi,ai,bi; inline void in() { scanf("%d%d%d%d",&xi,&yi,&ai,&bi); } }yb[sj]; int comp(const YB&a,const YB&b) { return a.ai<b.ai; } struct B { int ne,v,w; }b[sj*2]; void bj(int
&x,int y) { x=x<y?x:y; } int ma(int x,int y) { return x>y?x:y; } void add(int x,int y,int z) { b[e].v=y; b[e].w=z; b[e].ne=h[x]; h[x]=e++; } void spfa() { int st; while(!q.empty()) { st=q.front(); q.pop(); r[st]=0; for(int i=h[st];i!=-1;i=b[i].ne) if(dis[b[i].v]>ma(b[i].w,dis[st])) { dis[b[i].v]=ma(b[i].w,dis[st]); if(!r[b[i].v]) { q.push(b[i].v); r[b[i].v]=1; } } } } int main() { scanf("%d%d",&n,&m); tp=1; for(int i=1;i<=m;i++) { yb[tp].in(); if(yb[tp].xi==yb[tp].yi) tp--; tp++; } m=tp-1; sort(yb+1,yb+m+1,comp); memset(dis,0x3f,sizeof(dis)); dis[1]=0; ans=0x7fffffff; tp=ans; memset(h,-1,sizeof(h)); for(int i=1;i<=m;i++) { if(yb[i].ai==yb[i-1].ai) continue; for(int j=i;j<=m;j++) { if(yb[i].ai==yb[j].ai) { add(yb[j].xi,yb[j].yi,yb[j].bi); add(yb[j].yi,yb[j].xi,yb[j].bi); if(!r[yb[j].xi]) q.push(yb[j].xi),r[yb[j].xi]=1; if(!r[yb[j].yi]) q.push(yb[j].yi),r[yb[j].yi]=1; } else break; } int temp=dis[n]; spfa(); if(dis[n]!=temp) bj(ans,dis[n]+yb[i].ai); } if(ans==tp) printf("-1\n"); else printf("%d",ans); return 0; }
magicforest

魔法森林[NOI2014]