圆方树和广义圆方树学习小记

圆方树,解决毒瘤仙人掌问题的利器。

有了圆方树,什么树上的算法都可以套在仙人掌上了,比如说点分治、树链剖分、虚树等等。

OI数据结构无穷无尽,只有你不会的,没有你想不到的。

资料参见WC2017讲稿。

圆方树:

圆方树分为圆点和方点。

圆点就是原来有的点,方点是新加的点。

对于每一个点双联通分量,这个分量里面的边要删掉,分量里的所有点往分量对应的方点连边,分量出去的边照连。

可以证明这样还是一棵树,然后就可以根据题意乱搞了。

广义圆方树:

把圆方树建到无向图去。

其实是一样的,依然是对每一个点双联通分量搞搞。

tarjan要写得好。

裸题:
「SDOI2018」战略游戏

广义圆方树+虚树

Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define mem(a) memset(a, 0, sizeof a)
#define min(a, b) ((a) < (b) ? (a) : (b))
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;

const int N = 4e5 + 5;

int T, n, m, Q, x, y, td;
int dfn[N], low[N], d[N], z[N], tt, z0;

struct edge {
    int final[N], to[N], next[N], tot;
    void link(int x, int y) {
        next[++ tot] = final[x], to[tot] = y, final[x] = tot;
        next[++ tot] = final[y], to[tot] = x, final[y] = tot;
    }
    void cl() {
        mem(final); mem(next); tot = 1;
    }
} e, e2;

void tar(int x, int la) {
    dfn[x] = low[x] = ++ tt;
    z[++ z[0]] = x;
    for(int i = e.final[x]; i; i = e.next[i]) {
        int y = e.to[i];
        if(!dfn[y]) {
            tar(y, i);
            low[x] = min(low[x], low[y]);
            if(low[y] >= dfn[x]) {
                td ++;
                while(z[z[0]] != y) e2.link(z[z[0] --], td);
                e2.link(z[z[0] --], td); e2.link(x, td);
            }
        } else if(i != (la ^ 1)) low[x] = min(low[x], dfn[y]);
    }
}

int bz[N], fa[18][N], dep[N], s[N], p[N], q[N], tp;

void dfs(int x) {
    bz[x] = 1;
    s[x] += (x <= n);
    p[x] = ++ tp;
    for(int i = e2.final[x]; i; i = e2.next[i]) {
        int y = e2.to[i]; if(bz[y]) continue;
        fa[0][y] = x; dep[y] = dep[x] + 1; s[y] = s[x];
        dfs(y);
    }
    q[x] = tp;
    bz[x] = 0;
}

int lca(int x, int y) {
    if(dep[x] < dep[y]) swap(x, y);
    fd(i, 17, 0) if(dep[fa[i][x]] >= dep[y]) x = fa[i][x];
    if(x == y) return x;
    fd(i, 17, 0) if(fa[i][x] != fa[i][y]) x = fa[i][x], y = fa[i][y];
    return fa[0][x];
}

int cmp(int x, int y) {
    return p[x] < p[y];
}

int ff[N];

int main() {
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    for(scanf("%d", &T); T; T --) {
        e.cl(); e2.cl();
        scanf("%d %d", &n, &m);
        fo(i, 1, m) {
            scanf("%d %d", &x, &y);
            e.link(x, y);
        }
        mem(dfn);
        td = n; tt = 0; tar(1, 0);
        dep[1] = 1; s[1] = 0; tp = 0;
        mem(bz); dfs(1);
        fo(i, 1, 17) fo(j, 1, td) fa[i][j] = fa[i - 1][fa[i - 1][j]];
        for(scanf("%d", &Q); Q; Q --) {
            int k; scanf("%d", &k);
            fo(i, 1, k) scanf("%d", &d[i]), bz[d[i]] = Q;
            sort(d + 1, d + k + 1, cmp);
            int d0 = k;
            fo(i, 2, k) d[++ d0] = lca(d[i - 1], d[i]);
            sort(d + 1, d + d0 + 1, cmp);
            k = 0; fo(i, 1, d0) if(i == 1 || d[i] != d[i - 1]) d[++ k] = d[i];
            z0 = 0;
            fo(i, 1, k) {
                int x = d[i];
                if(x == 1) {z[++ z0] = 1; continue;}
                while(z0 && (p[x] < p[z[z0]] || p[x] > q[z[z0]]))
                    ff[z[z0]] = z[z0 - 1], z0 --;
                z[++ z0] = x;
            }
            while(z0) ff[z[z0]] = z[z0 - 1], z0 --;
            int ans = 0;
            fo(i, 1, k) {
                int x = d[i];
                if(i == 1) {ans += (bz[x] != Q && x <= n); continue;}
                ans += s[x] - s[ff[x]] - (bz[x] == Q && x <= n);
            }
            printf("%d\n", ans);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值