• 战略游戏 SDOI2018 圆方树 + 树上倍增求点权和


    AC通道!

    对于这道题, 我们如何才能使得两个点变得不连通呢?

    当然是干掉我们路径上必须经过的点,Which is called 割点

    而这里也就要利用到我们圆方树的性质。 这些割点就是我们圆方树上的圆点。

    于是,我们轻松的想到一个办法: 直接找出所有的圆点不就好了? 然鹅,我们的时间复杂度这样是过不去的。

    那么,如何快速求出所有圆点呢? 不妨换一种思路。 对于一个圆方树,如果我们能找到其包含所有点的连通块(当然是最小的,类似于最小生成树的东西),然后将其减掉s,就是我们的圆点的数量。

    (画图手摸)

    如果我们将所有的点按照dfs序排序(即dfn),然后依次遍历(请自行画图),发现我们将会得到一个联通图。这不就是我们要找的吗!!

    那么,如何统计其大小呢? 也很简单。令 val[方点] = 0, val[圆点] = 0,然后倍增求路径点权和即可。

    友情tips:不要忘了,LCA会被多次经过,要记得减掉。1号点和s号点的LCA不会被统计到,记得特判其圆点方点,然后加上。

    #include <bits/stdc++.h>
    using namespace std;
    #define N 1000010
    #define isdigit(c) ((c)>='0'&&(c)<='9')
    #define I_WANT_TO_AK_IOI 0
    
    int read(){
        int x = 0, s = 1;
        char c = getchar();
        while(!isdigit(c)){
            if(c == '-')s = -1;
            c = getchar();
        }
        while(isdigit(c)){
            x = (x << 1) + (x << 3) + (c ^ '0');
            c = getchar();
        }
        return x * s;
    }
    
    struct tu{  /*  “图 ” */
        int v, next;
    };
    int n, m;
    
    struct node{
        tu t[N << 1];
        int f[N << 1];
        int bian;
    
        inline void add(int u, int v){  /*双向边*/ 
            t[++bian] = (tu){v, f[u]}, f[u] = bian;
            t[++bian] = (tu){u, f[v]}, f[v] = bian;
            return ;
        }
        /*  调试错误 
        void out(){
            this-> bian; 
            printf("%d
    ", bian);
            return ;
        }*/
        void clean(){
            bian = 0;
            memset(f, 0, sizeof(f));
            for(int i = 1;i <= (N << 1); i++)
                t[i] = (tu){0, 0};
            return ;
        }
    }G, T;
    
    struct bcc{
        int dfn[N << 1], low[N], stac[N], top;
        int bcccnt, id;
    
        void tarjan(int now){    /*求圆方树*/
            dfn[now] = low[now] = ++id;
            stac[top++] = now;
            for(int i = G.f[now]; i; i = G.t[i].next){
                int v = G.t[i].v;
                if(!dfn[v]){
                    tarjan(v);
                    low[now] = min(low[now], low[v]);
                    if(dfn[now] <= low[v]){
                        bcccnt++;
                        do{
                            T.add(bcccnt, stac[--top]);
                        } while(stac[top] != v);  
                        T.add(bcccnt, now);  /*圆方树建图*/
                    }
                }
                else low[now] = min(low[now], dfn[v]);
            }
            return ;
        }
    
        inline void clean(){
            id = 0;
            top = 0;
            bcccnt = 0;
            memset(dfn, 0, sizeof(dfn));
            memset(low, 0, sizeof(low));
            return ;
        }
    } BCC;
    
    namespace LCA{
        int size[N << 1], deth[N << 1], dfn[N << 1], fa[N << 1][22], id = 0;
        int val[N], siz[N];
        void dfs(int now,int father){
            size[now] = 1;
            deth[now] = deth[father] + 1; 
            dfn[now] = ++id;
            fa[now][0] = father;
            val[now] = val[father] + (now <= n);
            for(int i = T.f[now]; i; i = T.t[i].next){
                int v = T.t[i].v;
                if(v != father){
                    dfs(v, now);
                    siz[now] += siz[v];
                }
            }
            return ;
        }
    
        void prepare(){  /*倍增预处理*/
            for(int j = 1;j <= 20; j++)
                for(int i = 1;i <= BCC.bcccnt; i++)
                    fa[i][j] = fa[fa[i][j-1]][j-1];
            return ;
        }
    
        int get_lca(int x, int y){  /*倍增求LCA*/
            if(deth[x] < deth[y]) swap(x, y);
            for(int i = 20; ~i; i--){
                if(deth[fa[x][i]] >= deth[y]) x = fa[x][i];
            }
            if(x == y) return x;
            for(int i = 20; ~i; i--){
                if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
            }
            return fa[x][0];
        }
    
        inline bool cmp(int x, int y){
            return dfn[x] < dfn[y];
        }
    
        inline void clean(){
            id = 0;
            memset(dfn, 0, sizeof (dfn));
            memset(fa, 0, sizeof(fa));
            memset(val,0, sizeof (val));
            memset(deth, 0, sizeof(deth));
            memset(size, 0, sizeof(size));
            return ;
        }
    
    }
    
    inline void clean(){
        BCC.clean();
        T.clean();
        G.clean();
        LCA::clean();
        return ;
    }
    int a[N];
    
    int main(){
    //  freopen("hh.txt", "r", stdin);
        int test = read();
        while(test--){
            n = read(), m = read();
            for(int i = 1;i <= m; i++){
                int x = read(), y = read();
                G.add(x, y);  /*先建原图*/ 
            }
            BCC.bcccnt = n;
            BCC.tarjan(1);  /*建立圆方树*/ 
            LCA::dfs(1, 0);
            LCA::prepare(); /*处理倍增和路径点权和*/ 
            int q = read();
            while(q--){
                int s = read(), ans = 0;
                for(int i = 1;i <= s; i++) a[i] = read();
                sort(a + 1, a + s + 1, LCA::cmp);  /*按照dfn排序*/ 
                a[s + 1] = a[1];
                for(int i = 1;i <= s; i++){
                    int lca = LCA::get_lca(a[i], a[i + 1]);
                    ans += LCA::val[a[i]] + LCA::val[a[i + 1]] - (LCA::val[lca] << 1);
                }
                ans >>= 1;
                ans -= s;
                ans += (LCA::get_lca(a[1], a[s]) <= n);
                printf("%d
    ", ans);
            }
            clean();  /*不要忘了清空呀*/
        }
        return I_WANT_TO_AK_IOI;
    }

    首道黑题,也是首次尝试大量的代码分段

  • 相关阅读:
    unrecognized selector sent to class
    Xcode 7安装KSImageNamed 不启作用
    使用IntelliJ IDEA配置Erlang开发环境
    BN 详解和使用Tensorflow实现(参数理解)
    argparse 在深度学习中的应用
    转置卷积的详细理解
    递归该怎么写(二)
    递归该怎么写(一)
    二叉树的遍历python 代码
    经典排序的python实现
  • 原文地址:https://www.cnblogs.com/wondering-world/p/13054928.html
Copyright © 2020-2023  润新知