您的位置:首页 > 其它

HYSBZ - 3732 最小生成树+倍增Lca

2018-03-28 20:27 393 查看
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3732
码了一个星期终于过了这一题。(渣是原罪...)
最小生成树+倍增lca 
思路不难,但这是我第一次写lca的题目。下面一步步来解释这题。
题意:给你m条无向边,有k个查询,问从a到b所有路径上的最长边的最小值。
首先,从a到b所有路径上的最长边的最小值所对应的边一定在最小生成树上。
为什么?
现在来回忆一下Kru的做法。把所有边从小到大排序,接着从小到大使用这些边去联通每个点,当使用一条边x时,若x的两个端点在此之前已经联通,则说明这两个点在此之前已经被更短的边联通。可以想象,最小生成树上每两个点都是被最短的边联通的。即当两个点联通时的最长边不可能更小。
所以,现在问题转化为从a到b的在最小生成树上的路径的最长边是多少。
可知从a到b的最长边等于max(a->最近公共祖先c的最长边,b->c的最长边),即题目转化为lca问题。
至此,思路就很明确了。先构建最小生成树,再寻找两个点的最近公共祖先。
本蒟蒻用倍增lca寻找最近公共祖先,关于倍增lca的解释可以看以下博客(https://riteme.github.io/blog/2016-2-1/lca.html)。
我们设mx[i][j]表示点i到fa[i][j]的最长边,与fa[i][j]=fa[fa[i][j-1]][j-1]相似mx[i][j]=max(mx[i][j-1],mx[fa[i][j-1]][j-1]),即点i到fa[i][j]的最长边等于点i到fa[i][j-1]的最长边与点u(fa[i][j-1])到点fa[u][j-1]的最长边的最大值。(如图)



代码:#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <string>
#include <queue>
#include <map>
#include <vector>
#include <bitset>
#include <set>

using namespace std;

typedef long long LL;
typedef pair<int,int> pii;
const int maxn=3e4+20;
const int maxm=2700+10;
const int maxb=25;
const int mod=1e9+7;
const int inf=1e9+7;
const double infD=1000000007.0;
const double eps=0.00000001;

typedef struct Edge
{
int x,y,w;
Edge() {}
Edge(int _x,int _y,int _w)
{
x=_x;
y=_y;
w=_w;
}
bool operator <(const struct Edge &r)const
{
return w<r.w;
}
} Edge;
typedef struct Node
{
int v,w;
Node() {}
Node(int _v,int _w)
{
v=_v;
w=_w;
}
} Node;
int n,m,Q;
int p[maxn];
Edge e[maxn];
vector<Node> G[maxn];
bool vis[maxn];
int deep[maxn],fa[maxn][maxb],mx[maxn][maxb];

int Find(int x)
{
if(p[x]==x) return x;
else return p[x]=Find(p[x]);
}

void Kru()
{
for(int i=1; i<=n; i++) p[i]=i,G[i].clear();
sort(e,e+m);
int cnt=0;
for(int i=0; i<m; i++)
{
if(cnt==n-1) break;
int x=Find(e[i].x),y=Find(e[i].y);
if(x==y) continue;
cnt++;
p[y]=x;
G[e[i].x].push_back(Node(e[i].y,e[i].w));
G[e[i].y].push_back(Node(e[i].x,e[i].w));
}
}

void dfs(int u,int d)
{
vis[u]=1;
deep[u]=d;
int len=G[u].size();
for(int i=0; i<len; i++)
{
int v=G[u][i].v;
if(!vis[v])
{
fa[v][0]=u;
mx[v][0]=G[u][i].w;
dfs(v,d+1);
}
}
}

void init()
{
for(int j=1; j<maxb; j++)
{
for(int i=1; i<=n; i++)
{
int x=i;
if(~fa[x][j-1])
{
fa[x][j]=fa[fa[x][j-1]][j-1];
mx[x][j]=max(mx[x][j-1],mx[fa[x][j-1]][j-1]);
}
}
}
}

int lca(int u,int v)
{
int ans=0;
if(deep[u]<deep[v]) swap(u,v);
for(int i=maxb-1; i>=0; i--)
{
if(deep[fa[u][i]]>=deep[v])
{
ans=max(ans,mx[u][i]);
u=fa[u][i];
}
}
if(u==v) return ans;
for(int i=maxb-1; i>=0; i--)
{
if(fa[u][i]!=fa[v][i])
{
ans=max(ans,mx[u][i]);
ans=max(ans,mx[v][i]);
u=fa[u][i];
v=fa[v][i];
}
}
ans=max(ans,mx[u][0]);
ans=max(ans,mx[v][0]);
return ans;
}

void work()
{
for(int i=0; i<m; i++)
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
Kru();

memset(vis,0,sizeof(vis));
memset(fa,0xff,sizeof(fa));
memset(mx,0,sizeof(mx));
memset(deep,0,sizeof(deep));
dfs(1,1);
init();

while(Q--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",lca(a,b));
}
}

int main()
{
#ifdef LOCAL
freopen("input.in","r",stdin);
#endif // LOCAL
// while(~scanf("%d%d%d",&n,&m,&Q))
// work();
scanf("%d%d%d",&n,&m,&Q);
work();
return 0;
}后记:
这道题听师兄说是可以直接构造出最小生成树,构造的时候和并查集一样,按秩合并,就能保证树高是logn的,就不用写倍增lca了。但是本蒟蒻实在是太渣了,写了很久都还是TLE,有兴趣的大佬可以试一下,随便教下弱弱的我。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: