[BZOJ]1095 捉迷藏 动态点分治(点分树)

1095: [ZJOI2007]Hide 捉迷藏

Time Limit: 40 Sec Memory Limit: 256 MB
Submit: 4152 Solved: 1756
[Submit][Status][Discuss]
Description

  捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩
捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋
子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的
时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要
求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两
个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房
间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的
距离。

Input

  第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,
表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如
上文所示。

Output

  对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关
着灯的,输出0;若所有房间的灯都开着,输出-1。

Sample Input

8

1 2

2 3

3 4

3 5

3 6

6 7

6 8

7

G

C 1

G

C 2

G

C 1

G
Sample Output

4

3

3

4
HINT

对于100%的数据, N ≤100000, M ≤500000。

题解

啊总算A了这道动态点分治了, 感觉点分树这个名字好高大上的样子…
相比于原来的点分, 动态点分治是建了一棵点分树, 每个重心向它子树里下一层重心连边, 就成了一棵点分树, 感觉跟支配树的构造有点相似. 由于重心所以树高是log级别的, 所以暴力跳支配树fa.
第一次写引用了黄学长的代码…
顺便粘一下hzwer的题解.

一开始写的可并堆感觉自己快炸了…
其实这种做法不难。。。就是代码长。。。
把每次分治的重心连成一棵树,树的深度是logn,每次修改一个结点只影响它到树根的一条链
这题具体实现的时候要维护三层堆
C.每个重心存所有子树到其距离
B.每个重心存各个子树最大值,即子结点堆C的最大值
A.全局一个堆,维护答案最大值,存每个堆B的最大值和次大值之和
树上距离用rmq来求比较优越2333

代码还是很清晰的, 虽然有180行….

#include<stdio.h>
#include<queue>
#include<algorithm>
#define Boc register char
#define Acce register int
#define Prio priority_queue<int> 
using namespace std;
const int P = 22;
const int inf = 1e9 + 7;
const int maxn = 1e5 + 5;
const int dblmaxn = 2e5 + 10;
int n, num, tot, idx, sum, root, T;
char ss[2];
bool vis[maxn], sta[maxn];
int st[P][dblmaxn], lg[dblmaxn], in[maxn], h[maxn], dep[maxn], f[maxn], fa[maxn], siz[maxn], pw[P];
inline const int read()
{
    Acce x = 0;
    Boc ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}
struct edge{ int nxt, v; }e[dblmaxn];
struct heap
{
    int fir, sec;
    Prio A, B;
    inline void push(int x) { A.push(x); }
    inline void erase(int x) { B.push(x); }
    inline void pop()
    {
        while(B.size() && A.top() == B.top())
            A.pop(), B.pop();
        A.pop();
    }
    inline int top()
    {
        while(B.size() && A.top() == B.top())
            A.pop(), B.pop();
        return (A.size()) ? A.top() : 0;
    }
    inline int size() { return A.size() - B.size();}
    inline int stop()
    {
        if (size() < 2) return 0;
        fir = top(); pop();
        sec = top(); push(fir);
        return sec;
    }
}A, B[maxn], C[maxn];
inline void add(int u, int v)
{
    e[++ num].v = v, e[num].nxt = h[u], h[u] = num;
    e[++ num].v = u, e[num].nxt = h[v], h[v] = num;
}
void dfs(int u, int tfa)
{
    in[u] = ++ idx;
    st[0][idx] = dep[u];
    for (int i = h[u]; i; i = e[i].nxt)
    {
        if (e[i].v == tfa) continue;
        dep[e[i].v] = dep[u] + 1;
        dfs(e[i].v, u);
        st[0][++ idx] = dep[u];
    }
}
void getrt(int u, int tfa)
{
    siz[u] = 1, f[u] = 0;
    for (int i = h[u]; i; i = e[i].nxt)
    {
        if (e[i].v == tfa || vis[e[i].v]) continue;
        getrt(e[i].v, u);
        siz[u] += siz[e[i].v];
        f[u] = max(f[u], siz[e[i].v]);
    }
    f[u] = max(f[u], sum - siz[u]);
    if (f[u] < f[root]) root = u;
}
void divi(int u, int dfa)
{
    fa[u] = dfa, vis[u] = true;
    for (int i = h[u]; i; i = e[i].nxt)
    {
        if (vis[e[i].v]) continue;
        sum = siz[e[i].v], root = 0;
        getrt(e[i].v, u), divi(root, u);
    }
}
inline int query(int u, int v)
{
    u = in[u], v = in[v];
    if (u > v) swap(u, v);
    int len = lg[v - u + 1];
    return min(st[len][u], st[len][v - pw[len] + 1]);
}
inline int dis(int u, int v)
{   return dep[u] + dep[v] - 2 * query(u, v);   }
inline void turn_off(int u)
{
    B[u].push(0);
    if (B[u].size() == 2) A.push(B[u].top());
    int v = u;
    while (fa[u])
    {

        int f = fa[u], D = dis(f, v), tmp = C[u].top();
        C[u].push(D);
        if (D > tmp)
        {
            int mx = B[f].top() + B[f].stop(), size = B[f].size();
            if (tmp) B[f].erase(tmp);
            B[f].push(D);
            int now = B[f].top() + B[f].stop();
            if (now > mx)
            {
                if (size >= 2) A.erase(mx); //means mx has contributed to A
                if (B[f].size() >= 2) A.push(now); //now
            }
        }
        u = f;
    }
}
inline void turn_on(int u)
{
    if (B[u].size() == 2) A.erase(B[u].top());
    B[u].erase(0);
    int v = u;
    while (fa[u])
    {
        int f = fa[u], D = dis(f, v), tmp = C[u].top();
        C[u].erase(D);
        if (D == tmp)
        {
            int mx = B[f].top() + B[f].stop(), size = B[f].size();
            B[f].erase(D);
            if(C[u].top()) B[f].push(C[u].top());
            int now = B[f].top() + B[f].stop();
            if(now < mx)
            {
                if(size >= 2) A.erase(mx);
                if(B[f].size() >= 2) A.push(now);
            }
        }
        u = f;
    }
}
inline void init()
{
    pw[0] = 1;
    for (int i = 1; i < P; ++ i) pw[i] = pw[i - 1] << 1;
    lg[0] = -1;
    for (int i = 1; i < dblmaxn; ++ i) lg[i] = lg[i >> 1] + 1;
}
int main()
{
    init();
    n = read();
    int u, v;
    for (Acce i = 1; i < n; ++ i)
        u = read(), v = read(), add(u, v);
    dfs(1, 0);
    for (Acce i = 1; i <= lg[idx]; ++ i)
        for (Acce j = 1; j + pw[i] - 1 <= idx; ++ j)
            st[i][j] = min(st[i - 1][j], st[i - 1][j + pw[i - 1]]);
    f[root = 0] = inf, sum = n;
    getrt(1, 0), divi(root, 0);
    for (Acce i = 1; i <= n; ++ i) C[i].push(0);
    for (Acce i = 1; i <= n; ++ i) turn_off(i), tot ++;
    T = read();
    for (Acce i = 1; i <= T; ++ i)
    {
        scanf("%s", ss);
        if (ss[0] == 'C')
        {
            int x = read();
            if (sta[x]) turn_off(x), tot ++;
            else turn_on(x), tot --;
            sta[x] ^= 1;
        }  else
        {
            if(tot <= 1) printf("%d\n", tot - 1);
            else printf("%d\n", A.top());
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值