[bzoj5329][loj2562][Sdoi2018]战略游戏【点双连通分量】【虚树】【圆方树】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=5329
  https://loj.ac/problem/2562
【题解】
  首先用求点双的方法建出圆方树,那么答案就是虚树中没有被用到的圆点数。
  关于如何求点双(圆方树):如果一个点的low不小于父亲的dfn,那么这个点就是割点,新建一个方点。一直弹栈直至把这个点弹出,弹栈的过程中与方点连边。同时父亲也要与方点连边。
  其实可以不用建虚树, d f s / 2 就是答案。
  时间复杂度 O ( S l o g N )
【代码】

# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       400010
# define    K       18
using namespace std;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}
struct Edge{
    int data, next;
}e[N * 3];
int head[N], place, use[N], num, ti, n, m, stp[N], top, topp,u[N * 2], v[N * 2], belong[N], low[N], dfn[N],
    dad[N][K + 1], ans, dep[N], q, p[N], deg[N];
vector <int> eg[N];
void build(int u, int v){
    e[++place].data = v; e[place].next = head[u]; head[u] = place;
}
void tarjan(int x){
    use[x] = true; low[x] = dfn[x] = ++ti; 
    stp[++top] = x; 
    for (int ed = head[x]; ed != 0; ed = e[ed].next){
        if (use[e[ed].data] == false){
            tarjan(e[ed].data);
            low[x] = min(low[x], low[e[ed].data]);
            if (low[e[ed].data] == dfn[x]){
                num++;
                while (stp[top] != e[ed].data) eg[num].push_back(stp[top--]);
                eg[num].push_back(stp[top--]);
                eg[num].push_back(x);
            }
        }
        else low[x] = min(low[x], dfn[e[ed].data]);
    }
}
void dfs(int x, int fa){
    dad[x][0] = fa, deg[x] = (x <= n) + deg[fa]; belong[x] = ++ti; dep[x] = dep[fa] + 1;
    for (int ed = head[x]; ed != 0; ed = e[ed].next)
        if (e[ed].data != fa && use[e[ed].data] != 2)
            dfs(e[ed].data, x);
}
void pre(int n){ 
    for (int i = 1, j = 1; j * 2 <= n; i++, j *= 2)
        for (int k = 1; k <= n; k++)
            dad[k][i] = dad[dad[k][i - 1]][i - 1];
}
bool cmp(int x, int y){
    return belong[x] < belong[y];
}
int lca(int u, int v){
    if (dep[u] > dep[v]) swap(u, v);
    for (int i = K; i >= 0; i--)
        if (dep[dad[v][i]] >= dep[u])
            v = dad[v][i];
    if (u == v) return u;
    for (int i = K; i >= 0; i--)
        if (dad[u][i] != dad[v][i])
            u = dad[u][i], v = dad[v][i];
    return dad[u][0];
}
int main(){
    for (int opt = read(); opt--;){ 
        memset(head, 0, sizeof(head)); 
        memset(use, 0, sizeof(use));
        place = 0, num = 0, ti = 0, top = 0;
        n = read(), m = read();
        for (int i = 1; i <= m; i++){
            u[i] = read(), v[i] = read();
            build(u[i], v[i]); build(v[i], u[i]);
        }
        tarjan(1);
        memset(head, 0, sizeof(head)); place = 0; ti = 0;
        for (int i = 1; i <= num; i++){
            int tmp = i + n;
            if (eg[i].size() == 2){
                build(eg[i][0], eg[i][1]), build(eg[i][1], eg[i][0]);
            }
            else {
            for (unsigned j = 0; j < eg[i].size(); j++)
                build(eg[i][j], tmp), build(tmp, eg[i][j]);
            }    
            eg[i].clear();
        }
        memset(dad, 0, sizeof(dad));
        ti = 0; dfs(1, 0); 
        pre(n + num);
        q = read();
        for (int i = 1; i <= q; i++){
            num = read(), ans = -2 * num;
            for (int j = 1; j <= num; j++) p[j] = read();
            sort(p + 1, p + num + 1, cmp);
            p[num + 1] = p[1];
            for (int j = 1; j <= num; j++){
                int tmp = lca(p[j], p[j + 1]);
                ans = ans + deg[p[j]] + deg[p[j + 1]] - 2 * deg[tmp];
            }
            ans /= 2;
            int tmp = lca(p[1], p[num]);
            if (deg[tmp] != deg[dad[tmp][0]]) ans++;
            printf("%d\n", ans);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/D_Vanisher/article/details/80569848