Codeforces Round #809 (Div. 2) E. Qpwoeirut and Vertices kruskal重构树+LCA+ST表

  • 给你一张图,若干次查询,每次查询询问 [ l , r ] [l,r] [l,r]之间所有点对,问最少需要前几条边能够使点联通
  • 基础好题,很巧妙,首先建立 k r u s k a l kruskal kruskal重构树,新添加的节点的点权表示它是第几条边,然后用 S T ST ST表维护 [ i , i + 1 ] [i,i+1] [i,i+1] L C A LCA LCA的点权,区间查询最大值即可得出答案
  • 为什么这样是对的呢?根据 k r u s k a l kruskal kruskal重构树的性质,这些节点一定是编号较小的,连续区间内任意两点的 L C A LCA LCA一定包含在若干个 [ i , i + 1 ] [i,i+1] [i,i+1] L C A LCA LCA内部,因为是一个连通图
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

struct DSU{
  int n;
  vector<int> s;
  DSU(int _n): n(_n){
    s.resize(n);
    iota(s.begin(), s.end(), 0);
  }
  int FIND(int x){
    return x == s[x] ? x : s[x] = FIND(s[x]);
  }
};
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int t;
  cin >> t;
  while(t--){
    int n, m, q;
    cin >> n >> m >> q;
    DSU dsu(2 * n);
    vector<int> dep(2 * n);
    vector<int> val(2 * n);
    vector<vector<int> > g(2 * n);
    vector<vector<int> > fa(2 * n, vector<int>(30));
    vector<vector<int> > ST(2 * n, vector<int>(30));
    for(int i=0;i<m;i++){
      int u, v;
      cin >> u >> v;
      u -= 1;
      v -= 1;
      u = dsu.FIND(u);
      v = dsu.FIND(v);
      if(u != v){
        dsu.s[u] = dsu.s[v] = n;
        g[n].push_back(u);
        g[n].push_back(v);
        val[n] = i + 1;
        n += 1;
      }
    }
    n -= 1;
    function<void(int, int)> Dfs = [&](int u, int d){
      dep[u] = d;
      for(auto v : g[u]){
        fa[v][0] = u;
        Dfs(v, d + 1);
      }
    };
    Dfs(n, 1);
    function<int(int, int)> LCA = [&](int u, int v){
      if(dep[u] < dep[v]) swap(u, v);
      for(int i=log2(dep[u] - dep[v]);i>=0;i--){
        if((1 << i) <= dep[u] - dep[v]){
          u = fa[u][i];
        }
      }
      if(u == v) return val[u];
      for(int i=log2(dep[u]);i>=0;i--){
        if(fa[u][i] != fa[v][i]){
          u = fa[u][i];
          v = fa[v][i];
        }
      }
      return val[fa[u][0]];
    };
    for(int j=1;j<30;j++){
      for(int i=0;i<n;i++){
        fa[i][j] = fa[fa[i][j - 1]][j - 1];
      }
    }
    for(int i=0;i<n-1;i++){
      ST[i][0] = LCA(i, i + 1);
    }
    for(int j=1;j<=log2(n);j++){
      for(int i=0;i+(1<<(j-1))<n;i++){
        ST[i][j] = max(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);
      }
    }
    function<int(int, int)> Get = [&](int l, int r){
      int len = r - l + 1;
      int sz = log2(len);
      return max(ST[l][sz], ST[r - (1 << sz) + 1][sz]);
    };
    for(int i=0;i<q;i++){
      int l, r;
      cin >> l >> r;
      l -= 1;
      r -= 1;
      if(l == r){
        cout << 0 << " \n"[i == q - 1];
      }else{
        r -= 1;
        cout << Get(l, r) << " \n"[i == q - 1];
      }
    }
  }
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Clarence Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值