E. Qpwoeirut and Vertices
E. Qpwoeirut and Vertices
分析
分析起来,倒是不算太麻烦。
简单说明一下题意:
n
个点,m
条边的无向图。其中边按照其给的顺序赋边权,即为第一条给出的边,其边权为1,第二条给出的边,其边权为2,等。
给我们q
次询问,每次询问给一个范围[l,r]
,要求其中的点全部连通需要走的边集的最大边权的最小值。
一看到,无向图,最大边权最小值。直接Kruskal重构树。
首先,要求最大边权最小值,那一定升序建树。
接下来,我们考虑使得一堆点互相之间可以互相走到。
结论:一堆点互相之间可以走到,则只需要求这一堆点的LCA
,其权值即为我们满足两两互达的最大边权最小值。
对于这个的理解可以从下边两个角度。
- 我们知道,Kruskal重构树中除叶节点外的其余节点,都可以表示在限制内我们可以走到任意叶节点。则找到所有点的LCA,代表所有点都可以去其他点能经过的最大边权最小值。
- 当一个点其想到达其余点的时候所经过的最大边权最小值,就是其到每个点的路径中的最大边权最小值的最大值(因为可以绕路)。同理我们就可以得出对于一堆点其两两之间互达也就是求这一堆点的
LCA
。
但我们不能每次给一堆点,我们都去求这些点的LCA
,那时间复杂度我们不能接受。
我们就要用另外一个结论。
一堆点的LCA,即为其DFS序中最小的点与最大的点的LCA
如果这些点是乱序的,我们还是没办法。但是这些点是一个连续的区间,那我们就可以用st表
的时间求出来这个区间的dfs序
最大与最小的点。
则,这道题就结束了。
同时,给出一个引申问题。
将k
个点断开使得其互相不连通需要去掉的所有边集的最大边权的最小值是多少
问题来自这个题。我也写过一篇题解。
AC_code
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 2e5 + 10,M = 2e5 + 10;
int h[N],e[N],ne[N],w[N],idx;
int sz[N],son[N],dep[N],fa[N];
int dfn[N],top[N],ts;
int p[N];
int st1[20][N>>1],st2[20][N>>1],Log[N>>1];
int n,m,q;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
int find(int x)
{
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
void dfs1(int u)
{
sz[u] = 1;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
dep[j] = dep[u] + 1;fa[j] = u;
dfs1(j);
sz[u] += sz[j];
if(sz[j]>sz[son[u]]) son[u] = j;
}
}
void dfs2(int u,int tp)
{
top[u] = tp;dfn[u] = ++ts;
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==son[u]) continue;
dfs2(j,j);
}
}
int query1(int l,int r)
{
int k = Log[r-l+1];
return dfn[st1[k][l]]>dfn[st1[k][r-(1<<k)+1]]?st1[k][l]:st1[k][r-(1<<k)+1];
}
int query2(int l,int r)
{
int k = Log[r-l+1];
return dfn[st2[k][l]]<dfn[st2[k][r-(1<<k)+1]]?st2[k][l]:st2[k][r-(1<<k)+1];
}
int lca(int u,int v)
{
// int cnt = 0;
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u = fa[top[u]];
// cnt++;if(cnt>10000000) exit(0);
}
return dep[u]<dep[v]?u:v;
}
void solve()
{
cin>>n>>m>>q;
for(int i=1;i<=2*n;i++) h[i] = -1,p[i] = i,w[i] = 0,son[i] = 0;
ts = idx = 0;
int cnt = n;
for(int i=0;i<m;i++)
{
int u,v;cin>>u>>v;
int pa = find(u),pb = find(v),c = i + 1;
if(pa!=pb)
{
w[++cnt] = c;
p[pa] = p[pb] = cnt;
add(cnt,pa),add(cnt,pb);
}
}
fa[2*n-1] = 0,dep[2*n-1] = 0;
dfs1(2*n-1);
dfs2(2*n-1,2*n-1);
for(int j=0;j<20;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
{
if(!j)
{
st1[j][i] = i;
st2[j][i] = i;
}
else
{
st1[j][i] = dfn[st1[j-1][i]]>dfn[st1[j-1][i+(1<<j-1)]]?st1[j-1][i]:st1[j-1][i+(1<<j-1)];
st2[j][i] = dfn[st2[j-1][i]]<dfn[st2[j-1][i+(1<<j-1)]]?st2[j-1][i]:st2[j-1][i+(1<<j-1)];
}
}
while(q--)
{
int u,v;cin>>u>>v;
cout<<w[lca(query1(u,v),query2(u,v))]<<" ";
}
cout<<"\n";
}
int main()
{
ios;
int T;cin>>T;
Log[0] = -1;
for(int i=1;i<(N>>1);i++) Log[i] = ((i&(i-1))==0)?(Log[i-1]+1):Log[i-1];
while(T--)
{
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步