zoukankan      html  css  js  c++  java
  • 【题解】CF#983 E-NN country

      首先,我们从 u -> v 有一个明显的贪心,即能向上跳的时候尽量向深度最浅的节点跳。这个我们可以用树上倍增来维护。我们可以认为 u 贪心向上跳后不超过 lca 能跳到 u' 的位置, v 跳到 v' 的位置,这时只需要查询一下是否有 u' -> v' 的直达公交线路就可以确定出答案了。

      如果 u 和 v 在一条链上,我们可以直接倍增获得答案,所以以下讨论均为 u != lca && v != lca 的情况。 若在节点 u 和 v 之间存在一条直达线路,说明有一条线路的起点在以 u 为根的子树内,重点在以 v 为根的子树内。即起点的dfs序 >= dfn[u] && <= dfn[u] + size[u] - 1, 终点的 dfs序 >= dfn[v] && <= dfn[v] + size[v] -1;这是一个二维限制的问题,暴力主席树就可以搞定……

      之后看题解发现其实有一种妙妙的做法并不需要主席树。我们可以离线处理。要查询是否有起点在 u 的子树内且终点在 v 的子树内的路线,就是dfs在进入 u 的子树内之前记录下 v 的子树内此时记录的路径数,然后进入子树,遇到路线则标记终点所在的位置。返回之后两相对比,如果此时 v 的子树内记录的路径数有增长的话说明这样的路线是存在的。

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 450000
    #define INF 99999999
    #define CNST 20
    int n, m, q, Mul[maxn][CNST], gra[maxn][CNST];
    int dfn[maxn], dep[maxn], bits[30];
    int tot, timer, root[maxn], fa[maxn], size[maxn];
    
    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;
    }
    
    struct edge
    {
        int cnp, to[maxn], last[maxn], head[maxn];
        edge() { cnp = 2; }
        void add(int u, int v)
        { to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++; }
    }E1, G;
    
    struct edge1
    {
        int u, v;
        edge1(int _u = 0, int _v = 0) { u = _u, v = _v; }
        friend bool operator <(const edge1& a, const edge1& b)
        { return (dfn[a.u] != dfn[b.u]) ? dfn[a.u] < dfn[b.u] : dfn[a.v] < dfn[b.v]; }
    }e[maxn];
    
    struct node 
    { 
        int u, num; 
        node(int _u = 0, int _num = 0)
        { u = _u, num = _num; }
    };
    
    struct node1
    {
        int sum, ls, rs;
    }T[maxn * 28];
    
    void dfs(int u)
    {
        gra[u][0] = fa[u], dfn[u] = ++ timer; size[u] = 1;
        for(int i = 1; i < CNST; i ++) gra[u][i] = gra[gra[u][i - 1]][i - 1];
        for(int i = E1.head[u]; i; i = E1.last[i])
        {
            int v = E1.to[i];
            dep[v] = dep[u] + 1; dfs(v);
            size[u] += size[v];
        }
    }
    
    int dfs2(int u)
    {
        int lim = 0;
        for(int i = G.head[u]; i; i = G.last[i]) 
        {
            if(u != G.to[i]) lim = (dep[lim] < dep[G.to[i]]) ? lim : G.to[i];
            if(!lim && u != G.to[i]) lim = G.to[i];
        }
        for(int i = E1.head[u]; i; i = E1.last[i])
        {
            int v = E1.to[i];
            int t = dfs2(v); 
            if(u != t && t) lim = (dep[lim] < dep[t]) ? lim : t;
            if(!lim && u != t) lim = t;
        }
        Mul[u][0] = lim;
        return lim;
    }
    
    void dfs3(int u)
    {
        for(int i = 1; i < CNST; i ++)
            Mul[u][i] = Mul[Mul[u][i - 1]][i - 1];
        for(int i = E1.head[u]; i; i = E1.last[i])
            dfs3(E1.to[i]);
    }
    
    int LCA(int u, int v)
    {
        if(dep[u] < dep[v]) swap(u, v);
        for(int i = CNST - 1; ~i; i --) 
            if(dep[gra[u][i]] >= dep[v]) u = gra[u][i];
        for(int i = CNST - 1; ~i; i --)
            if(gra[u][i] != gra[v][i]) u = gra[u][i], v = gra[v][i];
        return (u == v) ? u : gra[u][0];
    }
    
    node Jump(int u, int fu)
    {
        bool flag = 0; int cnt = 0; 
        if(dep[u] == dep[fu]) flag = 1;
        for(int i = CNST - 1; ~i; i --)
        {
            if(!Mul[u][i]) continue;
            if(dep[Mul[u][i]] <= dep[fu]) flag = 1;
            if(dep[Mul[u][i]] > dep[fu]) u = Mul[u][i], cnt += bits[i];
        }
        if(!flag) return node(-1, 0);
        else return node(u, cnt);
    }
    
    void Ins(int &now, int pre, int l, int r, int x)
    {
        now = ++ tot; T[now] = T[pre]; T[now].sum ++;
        if(l == r) return; 
        int mid = (l + r) >> 1;
        if(x <= mid) Ins(T[now].ls, T[pre].ls, l, mid, x);
        else Ins(T[now].rs, T[pre].rs, mid + 1, r, x);
    }
    
    bool Query(int now, int pre, int l, int r, int L, int R)
    {
        if(!now) return 0;
        if(L == l && R == r) return (T[now].sum - T[pre].sum);
        if(L > r || R < l) return 0;
        int mid = (l + r) >> 1;
        if(R <= mid) return Query(T[now].ls, T[pre].ls, l, mid, L, R);
        else if(L > mid) return Query(T[now].rs, T[pre].rs, mid + 1, r, L, R);
        else
        {
            if(Query(T[now].ls, T[pre].ls, l, mid, L, mid)) return 1;
            else return Query(T[now].rs, T[pre].rs, mid + 1, r, mid + 1, R);
        }
    }
    
    int main()
    {
        n = read();
        bits[0] = 1; for(int i = 1; i < CNST; i ++) bits[i] = bits[i - 1] << 1;
        for(int i = 2; i <= n; i ++)
        {
            fa[i] = read();
            E1.add(fa[i], i);
        }
        dep[1] = 1; dfs(1);
        m = read();
        for(int i = 1; i <= m; i ++)
        {
            int u = read(), v = read(), lca = LCA(u, v);
            G.add(u, lca); G.add(v, lca);
            e[i] = edge1(u, v);
        }
        dfs2(1); dfs3(1);
        sort(e + 1, e + 1 + m); int last = 0; root[0] = tot = 1;
        for(int i = 1; i <= m; i ++)
        {
            while(last < dfn[e[i].u] - 1) T[root[last + 1] = ++ tot] = T[root[last]], last ++;
            Ins(root[dfn[e[i].u]], root[last], 1, n, dfn[e[i].v]);
            if(last != dfn[e[i].u]) last ++;
        }
        while(last + 1 <= n) T[root[last + 1] = ++ tot] = T[root[last]], last ++;
        q = read();
        for(int i = 1; i <= q; i ++)
        {
            int u = read(), v = read(), lca = LCA(u, v);
            node a = Jump(u, lca), b = Jump(v, lca); 
            int fu = a.u, fv = b.u;
            if(fu == -1 || fv == -1) { printf("-1
    "); continue; }
            if(u == lca || v == lca) { printf("%d
    ", a.num + b.num + 1); continue; }
            int l1 = dfn[fu], r1 = dfn[fu] + size[fu] - 1;
            int l2 = dfn[fv], r2 = dfn[fv] + size[fv] - 1;
            int x = Query(root[r1], root[l1 - 1], 1, n, l2, r2);
            int y = Query(root[r2], root[l2 - 1], 1, n, l1, r1);
            
            printf("%d
    ", a.num + b.num + 1 + !(x || y));
        }
        return 0;
    }
  • 相关阅读:
    linq in 查询
    sql数据分组取第一条
    获取mac地址
    计算机网络体系结构分层 (OSI TCP/IP)
    2048游戏代码
    go——变量、类型、常量、函数
    关于装饰器 开放封闭
    linux杂碎知识
    crawl——scrapy(配置文件,持久化,请求传递参数,提高爬虫效率,爬虫中间件,集成selenium,去重规则)
    crawl——xpath使用
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/9899205.html
Copyright © 2011-2022 走看看