P1967 [NOIP2013 提高組] 貨車運輸
阿新 • • 發佈:2022-04-22
首先能想到floyed暴力做法 60分是沒問題的
進一步 兩點之間路徑可能會有多條 但是我們只用找到路徑上最長邊最小的那條
但是因為有多個詢問 所以我們不能單方面考慮兩個點 而是考慮很多對兩個點
考慮建立最大生成樹 這樣的解一定是最優的!!!很巧妙
最後找兩點之間的最短的那條邊 用倍增lca 順便維護最短邊就好
注意可能最後建的圖可能是個森林!!!
這個題綜合性很強 是一道非常不錯的題目
#include<bits/stdc++.h> using namespace std; #define lowbit(x) x&(-x) #define ll long long #define inf 2e9+7 const int maxn=1e4+5; int n,m,cnt,T; int head[maxn],fa[maxn],dp[maxn],dad[maxn][21],minn[maxn][21],vis[maxn]; int find(int x){ if(x!=fa[x])return fa[x]=find(fa[x]); return x; } struct node{ int to,next,w,u; }ed[maxn*5],edg[maxn*5]; bool cmp(node a,node b){ return a.w>b.w; } void add(int u,int v,int w){ edg[++cnt].next=head[u];head[u]=cnt;edg[cnt].to=v;edg[cnt].w=w;edg[cnt].u=u; } void dfs(int,int); void lca(int,int); int main(){ cin>>n>>m; for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); ed[i].to=y;ed[i].u=x;ed[i].w=z; } sort(ed+1,ed+1+m,cmp); for(int i=1;i<=m;i++){ int u=ed[i].u,v=ed[i].to,w=ed[i].w; int fu=find(u),fv=find(v); if(fu!=fv){ add(u,v,w); add(v,u,w); fa[fv]=fu; } } for(int i=1;i<=n;i++) if(!vis[i]){ dp[i]=1; dad[i][0]=i; minn[i][0]=inf; dfs(i,i); } for(int j=1;j<21;j++) for(int i=1;i<=n;i++) dad[i][j]=dad[dad[i][j-1]][j-1],minn[i][j]=min(minn[i][j-1],minn[dad[i][j-1]][j-1]); cin>>T; while(T--){ int x,y; scanf("%d%d",&x,&y); lca(x,y); } return 0; } void dfs(int u,int f){ vis[u]=1; for(int i=head[u];i;i=edg[i].next){ int to=edg[i].to; if(to==f)continue; dad[to][0]=u; minn[to][0]=edg[i].w; dp[to]=dp[u]+1; dfs(to,u); } } void lca(int a,int b){ if(find(a)!=find(b)){ cout<<"-1"<<endl; return; } if(dp[a]<dp[b])swap(a,b); int res=inf; for(int i=20;i>=0;i--) if(dp[dad[a][i]]>=dp[b])res=min(res,minn[a][i]),a=dad[a][i];//一定要先跟新res再向上跳 if(a==b){ cout<<res<<endl; return; } for(int i=20;i>=0;i--) if(dad[a][i]!=dad[b][i]){ res=min(res,minn[a][i]); res=min(res,minn[b][i]); a=dad[a][i];b=dad[b][i]; } res=min(res,minn[a][0]); res=min(res,minn[b][0]); cout<<res<<endl; }