[HNOI2014]世界树【虚树构建+思维优化码量】

题目链接


  这道题,难就难在把思维理清楚了,只要理清楚之后写起来还是比较好写的,尽管码量是真的挺大的。

  我们可以给每个点去定义它是被哪个点所控制的。

(一)、求每个点是被哪个点所控制的

  因为我们建树会得到一棵虚树,所以我们要知道每个点都是由哪个点所控制的,什么叫做控制?就是距离它最近的被选取的点是哪个?如果本身就是,那么就是本身。这个我们可以由两次dfs来实现,第一个dfs求的是它的子儿子中,到达它最近的点,所以是先向下跑,再回来更新;第二个dfs是把它的最近子儿子看看能不能作为别的点的最近点,也就是将父亲的最近子儿子推下去。

(二)、每一条虚链是怎样被划分的

  每一条虚链上,实际上还是有点构成的,除非这条虚链的长度为1,那么上面就不会存在点了,接下去,如果虚链上有点,那么我们可以根据某个点来划分成两部分,一部分去给u点,而另一部分去给v点,于是就可以做到将这条虚链划分开来,当然有时候没法划分,就是u、v的控制点是一样的,这时候没法划分,直接整个给他们的控制点即可。

  有人可能会想,如果两个点都是虚树中的非关键点怎么办(也就是乱入的lca点)?那么,也没有关系,因为我们在上一步中已经确定了控制点,算贡献利用控制点算即可。

一组样例(很显然我不是1A咯…… 2A还算是行吧):

7
1 2
1 3
3 7
2 4
4 5
5 6
1
2
6 7
ans:4 3

码量挺长的,不过有少许注释,其实理解了写起来还挺方便,就是细节注意了。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 3e5 + 7;
int N, Q;
struct Build_Graph
{
    int head[maxN], cnt;
    struct Eddge
    {
        int nex, to, val;
        Eddge(int a=-1, int b=0, int c=0):nex(a), to(b), val(c) {}
    }edge[maxN << 1];
    inline void addEddge(int u, int v, int w)
    {
        edge[cnt] = Eddge(head[u], v, w);
        head[u] = cnt++;
    }
    inline void _add(int u, int v, int w) { addEddge(u, v, w); addEddge(v, u, w); }
    inline void init()
    {
        cnt = 0;
        for(int i=1; i<=N; i++) head[i] = -1;
    }
}Old, Now;
int deep[maxN], dfn[maxN], tot, root[maxN][20], LOG2[maxN], son[maxN];
inline bool cmp(int e1, int e2) { return dfn[e1] < dfn[e2]; }
void pre_dfs(int u, int fa)
{
    deep[u] = deep[fa] + 1; root[u][0] = fa; dfn[u] = ++tot; son[u] = 1;
    for(int i=0; i < LOG2[N]; i++) root[u][i + 1] = root[root[u][i]][i];
    for(int i=Old.head[u], v; ~i; i=Old.edge[i].nex)
    {
        v = Old.edge[i].to;
        if(v == fa) continue;
        pre_dfs(v, u);
        son[u] += son[v];
    }
}
inline int _LCA(int u, int v)
{
    if(deep[u] < deep[v]) swap(u, v);
    int det = deep[u] - deep[v];
    for(int i=LOG2[det]; i>=0; i--)
    {
        if((det >> i) & 1) u = root[u][i];
    }
    if(u == v) return u;
    for(int i=LOG2[N]; i>=0; i--)
    {
        if(root[u][i] ^ root[v][i])
        {
            u = root[u][i];
            v = root[v][i];
        }
    }
    return root[u][0];
}
int vis[maxN] = {0}, ans[maxN] = {0}, qid[maxN], Stap[maxN], Stop;
inline void Insert(int u)
{
    if(u == Stap[Stop]) return;
    if(Stop <= 1) { Stap[++Stop] = u; return; }
    int lca = _LCA(Stap[Stop], u);
    if(lca == Stap[Stop]) { Stap[++Stop] = u; return; }
    while(Stop > 1 && dfn[lca] <= dfn[Stap[Stop - 1]])
    {
        Now.addEddge(Stap[Stop - 1], Stap[Stop], deep[Stap[Stop]] - deep[Stap[Stop - 1]]);
        Stop--;
    }
    if(lca ^ Stap[Stop])
    {
        Now.addEddge(lca, Stap[Stop], deep[Stap[Stop]] - deep[lca]);
        Stap[Stop] = lca;
    }
    Stap[++Stop] = u;
}
pair<int, int> control_id[maxN];    //控制点
void control_dfs(int u)
{
    if(vis[u]) control_id[u] = MP(u, 0);
    else control_id[u] = MP(0, INF);
    for(int i=Now.head[u], v; ~i; i=Now.edge[i].nex)
    {
        v = Now.edge[i].to;
        control_dfs(v);
        if(Now.edge[i].val + control_id[v].second < control_id[u].second) control_id[u] = MP(control_id[v].first, Now.edge[i].val + control_id[v].second);
        else if(Now.edge[i].val + control_id[v].second == control_id[u].second && control_id[u].first > control_id[v].first) control_id[u].first = control_id[v].first;
    }
}
void update_dfs(int u)
{
    for(int i=Now.head[u], v; ~i; i=Now.edge[i].nex)
    {
        v = Now.edge[i].to;
        if(control_id[u].second + Now.edge[i].val < control_id[v].second) control_id[v] = MP(control_id[u].first, control_id[u].second + Now.edge[i].val);
        else if(control_id[u].second + Now.edge[i].val == control_id[v].second && control_id[u].first < control_id[v].first) control_id[v].first = control_id[u].first;
        update_dfs(v);
    }
}
int Up_to_Point(int u, int det) //向上det步的点
{
    for(int i=LOG2[det]; i>=0; i--)
    {
        if((det >> i) & 1) u = root[u][i];
    }
    return u;
}
void query_dfs(int u)
{
    int sum = 0;
    for(int i=Now.head[u], v, up_len, up_id, all_len; ~i; i=Now.edge[i].nex)
    {
        v = Now.edge[i].to;
        query_dfs(v);
        sum += son[v];
        if(Now.edge[i].val == 1) continue;
        if(control_id[u].first == control_id[v].first) continue;
        all_len = control_id[u].second + control_id[v].second + Now.edge[i].val;
        if(all_len & 1) //中间偶数个点
        {
            up_len = (all_len - 1) / 2 - control_id[v].second;
            up_id = Up_to_Point(v, up_len);
            ans[vis[control_id[v].first]] += son[up_id] - son[v];
            sum += son[up_id] - son[v];
        }
        else    //中间奇数个点
        {
            up_len = all_len / 2 - control_id[v].second;
            if(control_id[v].first > control_id[u].first) up_len--;
            up_id = Up_to_Point(v, up_len);
            ans[vis[control_id[v].first]] += son[up_id] - son[v];
            sum += son[up_id] - son[v];
        }
    }
    ans[vis[control_id[u].first]] += son[u] - sum;
    Now.head[u] = -1;
}
inline void init()
{
    tot = 0;
    Old.init(); Now.init();
    for(int i=1, j = 2, k = 0; i<=N; i++)
    {
        if(i == j) { k++; j <<= 1; }
        LOG2[i] = k;
    }
}
int main()
{
    scanf("%d", &N);
    init();
    for(int i=1, u, v; i<N; i++)
    {
        scanf("%d%d", &u, &v);
        Old._add(u, v, 1);
    }
    deep[0] = 0;
    pre_dfs(1, 0);
    scanf("%d", &Q);
    int sz;
    while(Q--)
    {
        scanf("%d", &sz);
        for(int i=1; i<=sz; i++) { scanf("%d", &qid[i]); vis[qid[i]] = i; }
        sort(qid + 1, qid + sz + 1, cmp);
        Stap[Stop = 1] = 1;
        Now.cnt = 0;
        for(int i=1; i<=sz; i++) Insert(qid[i]);
        while(Stop > 1)
        {
            Now.addEddge(Stap[Stop - 1], Stap[Stop], deep[Stap[Stop]] - deep[Stap[Stop - 1]]);
            Stop --;
        }
        control_dfs(1);
        update_dfs(1);
        query_dfs(1);
        for(int i=1; i<=sz; i++) printf("%d%c", ans[i], i == sz ? '\n' : ' ');
        for(int i=1; i<=sz; i++) vis[qid[i]] = ans[i] = 0;
    }
    return 0;
}
发布了795 篇原创文章 · 获赞 955 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_41730082/article/details/104342188