货车运输(洛谷P1967)(倍增)
生活随笔
收集整理的這篇文章主要介紹了
货车运输(洛谷P1967)(倍增)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
傳送門
文章目錄
- 題目描述
- 題目解析
- 代碼
題目描述
題目解析
本題如果告訴你,城市形成了一棵樹,是不是就迎刃而解了呢?
本題的關鍵就是把稠密圖轉化為一片森林(就是可能有很多棵樹啦)
怎么轉化呢?
考慮哪些邊是沒用的
如果AB已經可以聯通且限重很大,再給我一條A到B限重很小的邊肯定沒有用了
換句話說,我們有用的邊就是本圖中的一座最大生成森林
(不明白這個詞的童鞋可以類比一下最小生成樹的概念)
怎么證明呢?
很容易
考慮怎么生成最大森林的:
每次選出未選的權值最大的邊,如果兩邊的點未聯通就連上
假設A到B的最好路徑有一條邊X不在這個森林上
那么這條邊的權值肯定大于森林上AB路徑的最小值Y(這樣森林上的路徑才不是最優的)
那么我們建造森林時肯定會使X在Y之前枚舉到
而此時AB不連通(因為枚舉到Y時還不連通)
那么應該把X加入森林中
和“X不在這個森林上"矛盾
證完啦(自己寫的證明,可能有些草率 )
轉化為樹上問題后就非常簡單
倍增,dfs序,樹剖,都可以啦(還有很多做法但我還不會。。。)
聯通判斷常規并查集就OK了
說起來還是倍增好寫
那就寫倍增啦
倍增第一次少取了一個min還掛掉了。。。
代碼
#include<bits/stdc++.h> using namespace std; #define ll long long #define mid ((l+r)>>1) #define ls k<<1 #define rs k<<1|1 const int N=2e5+100; const int M=2e4+100;int n,m,q; int a,b,c;struct node{int to,nxt,v; }p[N]; int fi[N],cnt=-1; void addline(int x,int y,int v){ // printf("addline:x=%d y=%d v=%d\n",x,y,v);p[++cnt]=(node){y,fi[x],v};fi[x]=cnt; }int fa[N]; int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);} void merge(int a,int b){int aa=find(a),bb=find(b);fa[aa]=bb; }int dep[N],f[N],up[N]; void dfs(int x,int ffa){f[x]=ffa;dep[x]=dep[ffa]+1;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==ffa) continue;up[to]=p[i].v;dfs(to,x);}return; } struct node2{int x,y,v;bool operator < (const node2 o)const {return v>o.v;} }e[N];struct node3{int pl,mn; }dp[M][20]; int mi[20]; void solve(){mi[0]=1;for(int i=1;i<=16;i++) mi[i]=mi[i-1]<<1;for(int i=1;i<=n;i++){dp[i][0].pl=f[i];dp[i][0].mn=up[i]; // printf("ok i=%d k=0 pl=%d mn=%d\n",i,dp[i][0].pl,dp[i][0].mn);}for(int k=1;k<=15;k++){for(int i=1;i<=n;i++){if(dep[i]>=mi[k]){dp[i][k].pl=dp[dp[i][k-1].pl][k-1].pl;dp[i][k].mn=min(dp[i][k-1].mn,dp[dp[i][k-1].pl][k-1].mn); // printf("i=%d k=%d pl=%d mn=%d\n",i,k,dp[i][k].pl,dp[i][k].mn);}}} }int lca(int x,int y){int ans=2e9;if(dep[y]>dep[x]) swap(x,y);for(int k=15;k>=0;k--){if(dep[x]-mi[k]<dep[y]) continue;ans=min(ans,dp[x][k].mn);x=dp[x][k].pl; // printf("ok x=%d y=%d ans=%d\n",x,y,ans);}for(int k=15;k>=0;k--){if(mi[k]>dep[x]) continue;if(dp[x][k].pl==dp[y][k].pl) continue;ans=min(ans,dp[x][k].mn);x=dp[x][k].pl;ans=min(ans,dp[y][k].mn);y=dp[y][k].pl; // printf("x=%d y=%d ans=%d\n",x,y,ans);}if(x!=y) ans=min(ans,min(dp[y][0].mn,dp[x][0].mn));//這里x和y的dp都要考慮取min,顯然不一樣啊!!!return ans; }int main(){memset(fi,-1,sizeof(fi));memset(dep,-1,sizeof(dep));scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) fa[i]=i;for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].v);sort(e+1,e+1+m);for(int i=1;i<=m;i++){int xx=find(e[i].x),yy=find(e[i].y);if(xx==yy) continue;merge(xx,yy);addline(e[i].x,e[i].y,e[i].v);addline(e[i].y,e[i].x,e[i].v);}for(int i=1;i<=n;i++){if(dep[i]==-1){dep[i]=0;dfs(i,0);}}solve();scanf("%d",&q);for(int i=1;i<=q;i++){scanf("%d%d",&a,&b);if(find(a)!=find(b)) printf("-1\n");else printf("%d\n",lca(a,b));}return 0; } /* 11 9 1 2 8 1 3 15 1 7 7 2 4 12 3 6 9 3 5 6 8 9 11 8 10 10 10 11 15 100 */ 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的货车运输(洛谷P1967)(倍增)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 模板:网络流(Dinic算法)
- 下一篇: 暗黑破坏神(背包)(内部模拟)