zoukankan      html  css  js  c++  java
  • P4197 Peaks [克鲁斯卡尔重构树 + 主席树][克鲁斯卡尔重构树学习笔记]

    Problem

    (Bytemountains)(n)座山峰,每座山峰有他的高度(h_i) 。有些山峰之间有双向道路相连,共(M)条路径,每条路径有一个困难值,这个值越大表示越难走,现在有(Q)组询问,每组询问询问从点(v)开始只经过困难值小于等于(x)的路径所能到达的山峰中第(k)高的山峰,如果无解输出(-1)

    kruskal重构树是用来解决一些诸如“查询从某个点开始 经过边权不超过(val)所能到达的节点”的问题
    所以很显然 在最小生成树上是最优的。。其他多余的边可以不需要考虑。。
    那我们在跑kruskal的时候重新建边。。
    克鲁斯卡尔重构树的思想就是在建最小生成树的时候不是直接连边 而是新建一个节点 并把这个节点的值设为边权 然后令两个连通块的代表点分别作为它的左右儿子 然后令这个新节点成为整个连通块的代表点
    即如果(u) (v)不连通
    (u) (v)两点连到新建节点上。顺便连边。然后就变成了一棵树。
    而这棵树满足一个性质 十分重要 树满足一个最优性…至于图…不放了…自己手动模拟一下就知道了啊…把边权化成点权然后求个[LCA]通常是这个套路吧…
    但是这题貌似不太一样…要按上一个主席树…

    sort(q + 1 , q + m + 1 , cmp) ;
      for( int i = 1 ; i <= n ; i ++ ) fa[i] = i ;
      tot = n ;
      for( int i = 1 ; i <= m ; i ++ ) {
        int u = find(q[i].u) , v = find(q[i].v) ;
        if(u == v) continue ;
        val[++ tot] = q[i].w ; fa[tot] = fa[u] = fa[v] = tot ;
        add(tot , u) ; add(tot , v) ;
      }
    

    所以把新建的树变成dfs序放到主席树上解决就可以了(在线)。。

    #include<bits/stdc++.h>
    using namespace std ;
    const int N = 2e5 + 5 ;
    const int M = 5e5 + 5 ;
    const int inf = INT_MAX ;
    struct node { int v , nxt ; } e[N << 1] ;
    struct Node { int u , v , w ; } q[M] , t[N << 1] ;
    int n , m , Q ;
    int rt[N] , h[N] ;
    int ls[N << 5] , rs[N << 5] , sum[N << 5] ;
    int val[N] ;
    int head[N] , cnt = 0 ;
    inline void add(int u , int v) { e[++ cnt] = { v , head[u]} ; head[u] = cnt ; }
    inline bool cmp(Node x , Node y) { return x.w < y.w ; }
    int fa[N] ; int tot ;
    inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]) ; }
    inline void kruskal() {
      sort(q + 1 , q + m + 1 , cmp) ;
      for( int i = 1 ; i <= n ; i ++ ) fa[i] = i ;
      tot = n ;
      for( int i = 1 ; i <= m ; i ++ ) {
        int u = find(q[i].u) , v = find(q[i].v) ;
        if(u == v) continue ;
        val[++ tot] = q[i].w ; fa[tot] = fa[u] = fa[v] = tot ;
        add(tot , u) ; add(tot , v) ;
      }
    }
    int f[N][22], L[N] , R[N] ;
    int b[N] , pop = 0 ;
    inline void dfs(int u , int fa) {
      f[u][0] = fa ;
      for( int i = 1 ; i <= 19 ; i ++ ) f[u][i] = f[f[u][i - 1]][i - 1] ;
      L[u] = pop ;
      if(! head[u]) { b[++ pop] = u ; R[u] = pop ; return ; }
      for( int i = head[u] ; i ; i = e[i].nxt) {
        int v = e[i].v ; dfs(v , u) ;
      } R[u] = pop ;
    }
    inline void build(int l , int r , int & o) {
      o = ++ cnt ;
      if(l == r) return ;
      int mid = l + r >> 1 ;
      build(l , mid , ls[o]) ; build(mid + 1 , r , rs[o]) ;
    }
    inline void upd(int l , int r , int pre , int & o , int x) {
      o = ++ cnt ;
      ls[o] = ls[pre] ; rs[o] = rs[pre] ; sum[o] = sum[pre] ;
      if(l == r) { sum[o] ++ ; return ; }
      int mid = l + r >> 1 ;
      if(x <= mid) upd(l , mid , ls[pre] , ls[o] , x) ;
      else upd(mid + 1 , r , rs[pre] , rs[o] , x) ;
      sum[o] = sum[ls[o]] + sum[rs[o]] ;
    }
    inline int find_fa(int x , int k) {
      for(register int i = 19 ; ~ i ; i --)
        if(val[f[x][i]] <= k) x = f[x][i] ;
      return x ;
    }
    inline int query(int a , int b , int l , int r , int k) {
      if(l == r)
        if(k == sum[b] - sum[a]) return l ;
        else return 0 ;
      int x = sum[rs[b]] - sum[rs[a]] , mid = l + r >> 1 ;
      if(x >= k) return query(rs[a] , rs[b] , mid + 1 , r , k) ;
      else return query(ls[a] , ls[b] , l , mid , k - x) ;
    }
    signed main() {
      ios :: sync_with_stdio(false) ;
      cin.tie(nullptr) ;
      cout.tie(nullptr) ;
      cin >> n >> m >> Q ;
      val[0] = inf ;
      for( int i = 1 ; i <= n ; i ++ ) cin >> h[i] ;
      for( int i = 1 ; i <= n ; i ++ ) t[i].u = i , t[i].w = h[i] ;
      sort(t + 1 , t + n + 1 , cmp) ;
      for( int i = 1 ; i <= n ; i ++ ) h[t[i].u] = i ;
      for( int i = 1 ; i <= m ; i ++ ) { cin >> q[i].u >> q[i].v >> q[i].w ; }
      kruskal() ;
      dfs(tot , tot) ; cnt = 0 ;
      build(1 , n , rt[0]) ;
      for( int i = 1 ; i <= pop ; i ++)
        upd(1 , n , rt[i - 1] , rt[i] , h[b[i]]) ;
      t[0].w = -1 ;
      for( int i = 1 ; i <= Q ; i ++) {
        int v , x , k ; cin >> v >> x >> k ;
        int fa = find_fa(v , x) ;
        int ans = query(rt[L[fa]] , rt[R[fa]] , 1 , n , k) ;
        cout << t[ans].w << '
    ' ;
      }
      return 0 ;
    }
    
    
  • 相关阅读:
    Qt之使用CQU库快速开发统一风格界面
    Springboot中使用redis进行api防刷限流
    SpringBoot使用拦截器、过滤器、监听器
    ElasticSearch中文分词器-IK分词器的使用
    里式替换原则——面向对象程序设计原则
    使用Spring中的PropertyPlaceholderConfigurer读取文件
    新版本SpringCloud sleuth整合zipkin
    解放双手,在PC端进行Android真机调试
    破解Android设备无法联调的谜题
    打python&adb组合拳,实现微信读书永久免费读
  • 原文地址:https://www.cnblogs.com/Isaunoya/p/11780762.html
Copyright © 2011-2022 走看看