zoukankan      html  css  js  c++  java
  • 【题解】HNOI2016树

      大概最近写的这些题目都是仿生的代码……在这里先说明一下。可能比起做题记录来说更加像是学习笔记吧。之所以这样做主要还是因为感受到最近做的很多题目自己会做的都比较简单,不会做的又不敢触及,虽然也有所进步、学习了一些新的算法,但大体而言还是在原地踏步的样子。于是想要多观摩一下他人做题的过程并加以记录,也能开拓自己的视野,重新整理出新的思路,学习新的代码技巧。

      这一道题目首先第一眼我们就可以放弃建出这棵树的想法:极端情况下树的节点可以达到n2的级别,1e10个节点光是建出来就已经不可接受,更别谈找lca求距离了。但我们注意到大树上的每一棵小树都是模板树的一部分,如果说只将每次复制的新树的根看做节点的话,本质不同的点最多也只有O(n)个。这里我们就形成了思路:将每一次复制出来的子树看做一个节点挂在大树上,形成一个树套树的结构。这样,我们可以利用倍增快速求出大树上节点与节点之间的距离,而位于小块(小模板树)内部的距离我们则单独处理。

      其实感觉有点类似分块的呀,大块就直接跳,小块暴力 : ) (代码仿生)

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 100005
    #define int long long
    int n, m, q; 
    
    int read()
    {
        int x = 0, k = 1;
        char c;
        c = getchar();
        while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * k;
    }
    
    namespace Small
    {
        int cnp = 1, cnt, tot, head[maxn], gra[maxn][19], a[maxn];
        int dep[maxn], root[maxn], L[maxn], R[maxn];
        
        struct node
        {
            int last, to;
        }E[maxn << 1];
        
        struct tree
        {
            int l, r, size;
        }T[maxn * 20];
        
        void add(int u, int v)
        {
            E[cnp].to = v, E[cnp].last = head[u], head[u] = cnp ++;
        }
        
        void dfs(int u)
        {
            L[u] = ++ cnt; a[cnt] = u;
            for(int i = 1; i < 19; i ++)
                gra[u][i] = gra[gra[u][i - 1]][i - 1];
            for(int i = head[u]; i; i = E[i].last)
            {
                int v = E[i].to;
                if(v == gra[u][0]) continue;
                dep[v] = dep[u] + 1;
                gra[v][0] = u;
                dfs(v);
            }
            R[u] = cnt;
        } 
        
        void update(int &now, int pre, int l, int r, int key)
        {
            now = ++ tot; T[now] = T[pre];
            T[now].size ++;
            if(l == r) return;
            int mid = (l + r) >> 1;
            if(key <= mid) update(T[now].l, T[pre].l, l, mid, key);
            else update(T[now].r, T[pre].r, mid + 1, r, key);
        }
        
        void Build()
        {
            for(int i = 1; i <= n; i ++)
                update(root[i], root[i - 1], 1, n, a[i]);
        }
        
        void work()
        {
            for(int i = 1; i < n; i ++)
            {
                int u = read(), v = read();
                add(u, v), add(v, u);
            }
            dep[1] = 0; dfs(1);
            Build();
        }
        
        int Size(int x) { return R[x] - L[x] + 1; }
        
        int query(int a, int b, int l, int r, int k)
        {
            if(l == r) return l;
            int mid = (l + r) >> 1, size = T[T[b].l].size - T[T[a].l].size;
            if(size >= k) return query(T[a].l, T[b].l, l, mid, k);
            else return query(T[a].r, T[b].r, mid + 1, r, k - size);
        }
        
        int ask(int rt, int k)
        {
            int l = L[rt], r = R[rt];
            return query(root[l - 1], root[r], 1, n, k);
        }
        
        int to(int a, int b) { return dep[a] - dep[b]; }
        
        int lca(int u, int v)
        {
            if(dep[u] < dep[v]) swap(u, v);
            for(int i = 18; ~i; i --) 
                if(dep[gra[u][i]] >= dep[v]) u = gra[u][i];
            for(int i = 18; ~i; i --)
                if(gra[u][i] != gra[v][i]) u = gra[u][i], v = gra[v][i];
            return u == v ? u : gra[u][0];
        }
        
        int dis(int u, int v)
        {
            int LCA = lca(u, v);
            return dep[u] + dep[v] - 2 * dep[LCA];
        }
    }
    
    namespace Big
    {
        int up, tnum, tl[maxn], tr[maxn];
        int dep[maxn], t_to[maxn], trt[maxn];
        int c[maxn][19], gra[maxn][19];
        
        int pos(int x)
        {
            int l = 1, r = tnum;
            while(l < r)
            {
                int mid = (l + r) >> 1;
                if(x < tl[mid]) r = mid - 1;
                else if(x > tr[mid]) l = mid + 1;
                else return mid;
            }
            return l;
        }
        
        void work()
        {
            up = n, tnum = 1, tl[1] = 1, tr[1] = n, trt[1] = 1;
            for(int i = 1; i <= m; i ++)
            {
                int a = read(), b = read();
                int P = pos(b);
                trt[++ tnum] = a; tl[tnum] = up + 1;
                up += Small :: Size(a); tr[tnum] = up;
                gra[tnum][0] = P; dep[tnum] = dep[P] + 1;
                t_to[tnum] = Small :: ask(trt[P], b - tl[P] + 1);  // 得到了b的编号 
                c[tnum][0] = Small :: to(t_to[tnum], trt[P]) + 1;
            }
            
            for(int j = 1; j < 18; j ++)
                for(int i = 1; i <= tnum; i ++) 
                {
                    gra[i][j] = gra[gra[i][j - 1]][j - 1];
                    c[i][j] = c[i][j - 1] + c[gra[i][j - 1]][j - 1];
                }
            
            for(int i = 1; i <= q; i ++)
            {
                int u = read(), v = read(), ret = 0;
                int posu = pos(u), posv = pos(v);
                if(dep[posu] < dep[posv])
                    swap(posu, posv), swap(u, v);
                int reu = Small :: ask(trt[posu], u - tl[posu] + 1);
                int rev = Small :: ask(trt[posv], v - tl[posv] + 1);
                
                if(posu == posv) 
                {
                    printf("%lld
    ", Small :: dis(rev, reu));
                    continue;
                }
                
                u = posu, v = posv;
                for(int i = 18; ~i; i --)
                    if(dep[gra[u][i]] > dep[v]) ret += c[u][i], u = gra[u][i];
            
                if(gra[u][0] == v)
                {
                    ret += 1;
                    u = t_to[u]; v = rev;
                    ret += Small :: dis(u, v);
                    ret += Small :: to(reu, trt[posu]);
                    printf("%lld
    ", ret);
                    continue;
                }
            
                if(dep[u] > dep[v]) ret += c[u][0], u = gra[u][0];
                for(int i = 18; ~i; i --) 
                    if(gra[u][i] != gra[v][i]) 
                    {
                        ret += c[u][i], ret += c[v][i];
                        u = gra[u][i], v = gra[v][i];
                    }
                ret += 2; 
                ret += Small :: dis(t_to[u], t_to[v]);
                ret += Small :: dis(reu, trt[posu]) + Small :: dis(rev, trt[posv]);
                printf("%lld
    ", ret);
            }
        }
    }
    
    signed main()
    {
        n = read(), m = read(), q = read();
        Small :: work();
        Big :: work();
        return 0;
    } 
  • 相关阅读:
    从命令行运行postman脚本
    Postman简单的接口测试
    请写出正则表达式(regex),取得下列黄色部分的字符串 TEL: 02-236-9655/9659 FAX:02-236-9654 (黄色部分即02-236-9655/9659 ) ( 测试面试题)
    okhttp 的使用
    GridView的簡單使用
    Fragment 中 ListView绑定ContextMenu
    charles的使用
    selenium元素定位(Java)
    App间相互跳转及图片分享
    微信模板消息的使用
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/8735027.html
Copyright © 2011-2022 走看看