【Luogu】P1967货车运输(最大生成森林+倍增LCA)
倍增LCA是个什么蛇皮原理啊,循环完了还得再往上跳一次才能到最近公共祖先
合着我昨天WA两次就是因为这个
建最大生成森林,因为图不一定是联通的,所以不一定是一棵树。这个地方用克鲁斯卡尔就好了
然后给这个森林跑一遍DFS,顺便倍增
然后对于每个询问跑LCA,倍增的时候已经顺便求出了最小边权,所以往上跳的同时更新答案。
代码如下
#include<cstdio> #include<cctype> #include<cstdlib> #include<cmath> #include<algorithm> #include<cstring> inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct EDGE{ int from,to,dis; bool operator <(const EDGE &a)const{ return dis>a.dis; } }que[1000010]; int father[100010]; struct Edge{ int next,to,dis; }edge[1000010]; int head[500010],num; inline void add(int from,int to,int dis){ edge[++num]=(Edge){head[from],to,dis}; head[from]=num; } int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; } inline void unionn(int x,int y){ x=find(x);y=find(y); father[y]=x; } int deep[100010]; int d[10010][33]; int s[10010][33]; void dfs(int x,int fa){ deep[x]=deep[fa]+1; for(int i=head[x];i;i=edge[i].next){ int to=edge[i].to; if(to==fa) continue; d[to][0]=x; s[to][0]=edge[i].dis; dfs(to,x); } } int cnt; int main(){ int n=read(),m=read(); for(int i=1;i<=m;++i){ int from=read(),to=read(),dis=read(); que[i]=(EDGE){from,to,dis}; } std::sort(que+1,que+m+1); for(int i=1;i<=n;++i) father[i]=i; for(int i=1;i<=m;++i){ int from=que[i].from,to=que[i].to,dis=que[i].dis; if(find(from)==find(to)) continue; unionn(from,to); add(from,to,dis); add(to,from,dis); if(++cnt==n-1) break; } for(int i=1;i<=n;++i) if(!deep[i]) dfs(i,i); for(int j=1;(1<<j)<=n;++j) for(int i=1;i<=n;++i){ d[i][j]=d[d[i][j-1]][j-1]; s[i][j]=std::min(s[d[i][j-1]][j-1],s[i][j-1]); } int Q=read(); for(int i=1;i<=Q;++i){ int from=read(),to=read(); if(find(from)!=find(to)){ printf("-1\n"); continue; } if(deep[from]<deep[to]) std::swap(from,to); int x=deep[from]-deep[to],ans=0x7fffffff; for(int j=0;(1<<j)<=x;++j) if((1<<j)&x){ ans=std::min(ans,s[from][j]); from=d[from][j]; } if(from==to){ printf("%d\n",ans); continue; } for(int j=log2(n);j>=0;--j) if(d[from][j]!=d[to][j]){ ans=std::min(ans,std::min(s[from][j],s[to][j])); from=d[from][j]; to=d[to][j]; } ans=std::min(ans,std::min(s[from][0],s[to][0])); printf("%d\n",ans); } return 0; }
完毕。