Luogu4606 SDOI2018 战略游戏 圆方树、虚树、链并

传送门


弱化版

考虑到去掉一个点使得存在两个点不连通的形式类似割点,不难想到建立圆方树。那么在圆方树上对于给出的关键点建立虚树之后,我们需要求的就是虚树路径上所有圆点的数量减去关键点的数量。

因为没有DP,所以其实没有必要将虚树建立起来,只需要维护一个链并就可以了。

#include
//This code is written by Itst
using namespace std;

inline int read(){
    int a = 0;
    char c = getchar();
    bool f = 0;
    while(!isdigit(c) && c != EOF){
        if(c == '-')
            f = 1;
        c = getchar();
    }
    if(c == EOF)
        exit(0);
    while(isdigit(c)){
        a = a * 10 + c - 48;
        c = getchar();
    }
    return f ? -a : a;
}

const int MAXN = 2e5 + 7;
int head[MAXN] , N , M , cnt , cntEd , ans;
int dfn[MAXN] , low[MAXN] , st[MAXN] , ts , top;
int ind[MAXN] , dep[MAXN] , TS;
vector < int > ch[MAXN];
int num[MAXN] , jump[MAXN][19];
struct Edge{
    int end , upEd;
}Ed[MAXN << 1];
struct cmp{
    bool operator ()(int a , int b){
        return ind[a] < ind[b];
    }
};
set < int , cmp > s;
set < int , cmp > :: iterator it1 , it2;

inline void addEd(int a , int b){
    Ed[++cntEd].end = b;
    Ed[cntEd].upEd = head[a];
    head[a] = cntEd;
}

void pop(int x , int bot){
    ++cnt;
    ch[cnt].clear();
    ch[x].push_back(cnt);
    do{
        ch[cnt].push_back(st[top]);
    }while(st[top--] != bot);
}

void tarjan(int x , int p){
    dfn[x] = low[x] = ++ts;
    st[++top] = x;
    ch[x].clear();
    for(int i = head[x] ; i ; i = Ed[i].upEd)
        if(Ed[i].end != p)
            if(!dfn[Ed[i].end]){
                tarjan(Ed[i].end , x);
                low[x] = min(low[x] , low[Ed[i].end]);
                if(low[Ed[i].end] >= dfn[x])
                    pop(x , Ed[i].end);
            }
            else
                low[x] = min(low[x] , dfn[Ed[i].end]);
}

void dfs(int x , int p){
    ind[x] = ++TS;
    dep[x] = dep[p] + 1;
    jump[x][0] = p;
    for(int i = 1 ; i <= 18 ; ++i)
        jump[x][i] = jump[jump[x][i - 1]][i - 1];
    num[x] = num[p] + (x <= N);
    for(int i = 0 ; i < ch[x].size() ; ++i)
        dfs(ch[x][i] , x);
}

inline int LCA(int x , int y){
    if(dep[x] < dep[y])
        swap(x , y);
    for(int i = 18 ; i >= 0 ; --i)
        if(dep[x] - (1 << i) >= dep[y])
            x = jump[x][i];
    if(x == y)
        return x;
    for(int i = 18 ; i >= 0 ; --i)
        if(jump[x][i] != jump[y][i]){
            x = jump[x][i];
            y = jump[y][i];
        }
    return jump[x][0];
}

inline void insert(int x){
    s.insert(x);
    it1 = it2 = s.find(x);
    int p = 0 , q = 0;
    if(it1 != s.begin())
        p = LCA(*(--it1) , x);
    if(++it2 != s.end())
        q = LCA(*it2 , x);
    p = dep[p] < dep[q] ? q : p;
    ans += num[x] - num[p];
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("out","w",stdout);
#endif
    for(int T = read() ; T ; --T){
        cnt = N = read();
        M = read();
        memset(head , 0 , sizeof(head));
        memset(dfn , 0 , sizeof(dfn));
        cntEd = ts = TS = top = 0;
        for(int i = 1 ; i <= M ; ++i){
            int a = read() , b = read();
            addEd(a , b);
            addEd(b , a);
        }
        tarjan(1 , 0);
        dfs(1 , 0);
        for(int Q = read() ; Q ; --Q){
            s.clear();
            ans = 0;
            int S = read() , t;
            for(int i = 1 ; i <= S ; ++i){
                int a = read();
                if(i == 1)
                    t = a;
                else
                    if(t != 1)
                        t = LCA(t , a);
                insert(a);
            }
            printf("%d\n" , ans - S - num[jump[t][0]]);
        }
    }
    return 0;
}

你可能感兴趣的:(Luogu4606 SDOI2018 战略游戏 圆方树、虚树、链并)