zoukankan      html  css  js  c++  java
  • 【题解】SDOI2018战略游戏

      被CNST的大小卡了好久。一定要开到18呀……

      首先,遇到这种带各种各样环的图先考虑是不是可以建立圆方树,在圆方树上求出答案。然后转化为圆方树之后,我们就将图转化到了树上。答案非常的明显:只要一个圆点位于一个节点到另一个节点的路径上,它就是一个可以选择的答案点。

      又观察到数据范围中给出的总和 <= & 多组询问的模式,立马联想到建立虚树。建立出了虚树,我们发现这棵虚树有一个非常妙妙的性质:所有的叶子节点均为指定点。这样的话,在这棵虚树上所有的点(从叶子到根的路径上的点,包括没有建出来的点)均为合法的答案。不过要注意到因为我们自动建立出了1点为根节点,所以要防止1没有被选择,要减去这一段非法的点数。

    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 200000
    #define CNST 19
    int n, m, K, tot, T;
    int timer, dfn[maxn], low[maxn];
    int dep[maxn], dis[maxn], gra[maxn][CNST];
    int ans, S[maxn], a[maxn];
    bool vis[maxn];
    
    struct edge
    {
        int cnp = 1, head[maxn], to[maxn * 2], last[maxn * 2];
        void add(int u, int v)
        {
            if(u == v) return;
            to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++;
            to[cnp] = u, last[cnp] = head[v], head[v] = cnp ++;
        }
        void Clear()
        {
            cnp = 1; memset(head, 0, sizeof(head));
        }
    }E1, E2, E3;
    
    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;
    }
    
    void Tarjan(int u)
    {
        dfn[u] = low[u] = ++ timer; S[++ S[0]] = u;
        for(int i = E1.head[u]; i; i = E1.last[i])
        {
            int v = E1.to[i];
            if(!dfn[v])
            {
                Tarjan(v); low[u] = min(low[u], low[v]);
                if(low[v] >= dfn[u])
                {
                    E2.add(++ tot, u); int x = 0;
                    do
                    {
                        x = S[S[0] --]; E2.add(tot, x);
                    }while(x != v);
                }
            }
            else low[u] = min(low[u], dfn[v]);
        }
    }
    
    void dfs(int u, int fa)
    {
        dis[u] = 0, dep[u] = 0;
        gra[u][0] = fa, dfn[u] = ++ timer, dep[u] = dep[fa] + 1; 
        if(u <= n) dis[u] += 1; dis[u] += dis[fa];
        for(int i = 1; i < CNST; i ++) gra[u][i] = gra[gra[u][i - 1]][i - 1];
        for(int i = E2.head[u]; i; i = E2.last[i])
        {
            int v = E2.to[i];
            if(v != fa) dfs(v, u);
        }
    }
    
    int LCA(int x, int y)
    {
        if(dep[x] < dep[y]) swap(x, y);
        for(int i = CNST - 1; ~i; i --) 
            if(dep[gra[x][i]] >= dep[y]) x = gra[x][i];
        for(int i = CNST - 1; ~i; i --)
            if(gra[x][i] != gra[y][i]) 
                x = gra[x][i], y = gra[y][i];
        return x == y ? x : gra[x][0];
    }
    
    bool cmp(int a, int b)
    {
        return dfn[a] < dfn[b]; 
    }
    
    void DP(int u, int fa)
    {
        for(int i = E3.head[u]; i; i = E3.last[i])
        {
            int v = E3.to[i];
            if(v == fa) continue; 
            DP(v, u); ans += dis[v] - dis[u]; 
        }
        E3.head[u] = 0;
    }
    
    void Work()
    {
        E3.cnp = 1; 
        K = read(), tot = 1, S[1] = 1, S[0] = 1;
        for(int i = 1; i <= K; i ++) a[i] = read();
        sort(a + 1, a + 1 + K, cmp);
        int L = a[1];
        for(int i = 1; i <= K; i ++)
        {
            int lca = LCA(S[S[0]], a[i]);
            L = LCA(L, a[i]);
            while(23333)
            {
                if(dep[lca] >= dep[S[S[0] - 1]])
                {
                    E3.add(S[S[0]], lca); S[0] --;
                    if(lca != S[S[0]]) S[++ S[0]] = lca;
                    break;
                }
                if(S[0]) E3.add(S[S[0]], S[S[0] - 1]), S[0] --;
            }
            S[++ S[0]] = a[i];
        }
        while(S[0] > 1) E3.add(S[S[0]], S[S[0] - 1]), S[0] --;
        ans = 0; DP(1, 0);
        if(L > n) printf("%d
    ", ans - dis[L] + 1 - K);
        else printf("%d
    ", ans - dis[L] + 2 - K);
    }
    
    void init()
    {
        E1.Clear(), E2.Clear(), timer = 0;
        memset(gra, 0, sizeof(gra)); 
    }
    
    int main()
    {
        scanf("%d", &T);
        while(T --)
        {
            init();
            tot = n = read(), m = read();
            for(int i = 1; i <= n; i ++) dfn[i] = low[i] = 0;
            for(int i = 1; i <= m; i ++)
            {
                int u = read(), v = read();
                E1.add(u, v);
            }
            S[0] = 0, Tarjan(1); 
            int Q = read();
            timer = 0; dfs(1, 0);
            for(int i = 1; i <= Q; i ++) Work();
        }
        return 0;
    }
  • 相关阅读:
    为什么少有人在Windows电脑上安OS X?
    Xamarin.iOS开发初体验
    MySQL MyISAM/InnoDB高并发优化经验
    windows系统上安装与使用Android NDK r8d(二)
    windows系统上安装与使用Android NDK r8d(一)
    Windows平台下如何使用Android NDK
    Xamarin 手动安装步骤+破解(最新版Xamarin V3)
    MONO,原来你是水中月
    剑客vs刀客 Java vs .NET
    终于理解了什么是LGPL
  • 原文地址:https://www.cnblogs.com/twilight-sx/p/9240537.html
Copyright © 2011-2022 走看看