冷静 清醒 直视|

艾特玖

园龄:3年11个月粉丝:12关注:7

E. Qpwoeirut and Vertices

E. Qpwoeirut and Vertices

分析

分析起来,倒是不算太麻烦。

简单说明一下题意:

n个点,m条边的无向图。其中边按照其给的顺序赋边权,即为第一条给出的边,其边权为1,第二条给出的边,其边权为2,等。

给我们q次询问,每次询问给一个范围[l,r],要求其中的点全部连通需要走的边集的最大边权的最小值。

一看到,无向图,最大边权最小值。直接Kruskal重构树。

首先,要求最大边权最小值,那一定升序建树。

接下来,我们考虑使得一堆点互相之间可以互相走到。

结论:一堆点互相之间可以走到,则只需要求这一堆点的LCA,其权值即为我们满足两两互达的最大边权最小值。

对于这个的理解可以从下边两个角度。

  1. 我们知道,Kruskal重构树中除叶节点外的其余节点,都可以表示在限制内我们可以走到任意叶节点。则找到所有点的LCA,代表所有点都可以去其他点能经过的最大边权最小值。
  2. 当一个点其想到达其余点的时候所经过的最大边权最小值,就是其到每个点的路径中的最大边权最小值的最大值(因为可以绕路)。同理我们就可以得出对于一堆点其两两之间互达也就是求这一堆点的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;
}

本文作者:艾特玖

本文链接:https://www.cnblogs.com/aitejiu/p/16564249.html

版权声明:本作品采用艾特玖许可协议进行许可。

posted @   艾特玖  阅读(34)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起