虚树

对一棵树多次选取某些点进行询问,我们称这些点为关键点,而其他点不影响答案,我们可以建立一棵虚树,只包含这些关键点以及连接他们的LCA。

inline bool cmp(int A, int B) { return dfn[A] < dfn[B]; }
int stk[Z], tp;
inline void un(int x, int y) { add(y, x, dep[x] - dep[y]); tp--; }
inline void insert(int x)
{
    if (!tp) { stk[++tp] = x; return; }
    int lca = LCA(stk[tp], x);
    while (tp >= 2 && dfn[stk[tp - 1]] >= dfn[lca]) un(stk[tp], stk[tp - 1]);
    if (lca != stk[tp]) un(stk[tp], lca), stk[++tp] = lca;
    stk[++tp] = x;
}

 在遍历过程中,对已经操作完的点直接清空数据

void dfs(int rt)
{
    sz[rt] = key[rt];
    for (re i = head[rt]; i; i = e[i].ne)
    {
        int son = e[i].v;
        dfs(son);
        ans1 += sz[son] * (k - sz[son]) * e[i].w;//经过这条边的贡献
        sz[rt] += sz[son];//关键点个数
    }
    head[rt] = key[rt] = 0;//清空
}
sandom main()
{
    n = read();
    for (re i = 1; i < n; i++)
    {
        int u = read(), v = read();
        add(u, v, 1), add(v, u, 1);
    }
    search(1, 0);
    connect(1, 1);
    for (re i = 1; i <= n; i++) head[i] = 0;
    int Q = read();
    while (Q--)
    {
        cnt = tp = 0;
        k = read();
        for (re i = 1; i <= k; i++) a[i] = read(), key[a[i]] = 1;
        sort(a + 1, a + 1 + k, cmp);
        for (re i = 1; i <= k; i++) insert(a[i]);
        while (tp > 1) un(stk[tp], stk[tp - 1]);
        ans1 = 0;
        dfs(stk[tp]);
        write(ans1);
    }
    return 0;
}

 

posted @ 2022-07-20 21:15  sandom  阅读(32)  评论(0编辑  收藏  举报