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,有兴趣的大佬可以试一下,随便教下弱弱的我。
码了一个星期终于过了这一题。(渣是原罪...)
最小生成树+倍增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,有兴趣的大佬可以试一下,随便教下弱弱的我。
相关文章推荐
- bzoj3732 Network 最小生成树+LCA+树上倍增
- BZOJ 3732 Network —— 最小生成树 + 倍增LCA
- 【bzoj3732】Network 最小生成树+倍增LCA
- 【bzoj4242】水壶 BFS+最小生成树+倍增LCA
- 【BJOI2010】次小生成树-最小生成树+倍增LCA
- Codeforces Round #423 Div. 2 F. Best Edge Weight (倍增法lca)(最小生成树mst)
- UVA11354[Bond] 倍增求LCA+Kruskal求最小瓶颈生成树
- CodeForces 609 E.Minimum spanning tree for each edge(最小生成树-Kruskal+在线倍增LCA)
- 【CodeForces】827 D. Best Edge Weight 最小生成树+倍增LCA+并查集
- 2015 年 蓝桥杯 A 组 C/C++ 第十题 灾后重建 【最小生成树 + LCA倍增 + 线段树维护区间max】
- bzoj3732 -- 最小生成树+倍增
- [BZOJ]3732: Network 最小生成树 LCA
- gym 101081 gym F. Auction of Services 最小生成树+倍增LCA
- bzoj3732: Network(倍增LCA+最小生成树)
- [bzoj3732][最小生成树][lca]Network
- 【UVa】11354 Bond 最小生成树,动态LCA,倍增思想
- UVA 11354 Bond(最小生成树+lca+倍增求祖先节点)
- UVa 11354 Bond 最小生成树+LCA倍增
- 【bzoj3732】 network 最小生成树+lca
- bzoj 3732: Network (最小生成树+LCA)