• [ZJOI 2007]Hide 捉迷藏


    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。

    题解

    如果这道题不修改,显然我们能够想到树形 $DP$ 或者点分来做。

    然而对于修改的,显然不能再用静态的做法实现。

    这里就使用一种新的思想:动态点分。

    它的主要思想就是通过将的重心相连来维护一棵“点分树”,通过“点分树”中各个节点与其儿子间的联系来实现在线修改和查询的目的。

    对于这道题:我们需要的就是对于每个重心,找到以这个重心为起点向其子树延伸出的最长链和次长链。

    我们可以考虑维护一个堆,来维护断开重心后形成的各个联通块中所有的黑点到重心的距离。

    由于最长链和次长链不能来自同一个联通块,我们考虑再维护一个堆来维护以它为重心分割出的各个联通块的保存距离的堆的堆顶。

    值得注意的是,重心自己如果是黑点的话是可以连向任意一个子树,所以不妨假设重心自己也是一个独立的联通块,所以一开始第二个堆中需要 $push(0)$ 。

    这第二个堆中的最大值和次大值(如果有的话)就是经过这个重心的最长路径。

    我们考虑再维护一个堆,来存第二个堆的堆顶,这样的话,第三个堆的堆顶就是询问的答案。

    对于修改操作,显然我们要将其被管辖的所有重心都要进行修改,这时我们只要暴力在“分治树”上跳就行了,因为“分治树”的深度不会大于 $log_2 n$ 层的。

    点分复杂度是 $log_2 n$ 的,堆的操作也是 $log_2 n$ 的,总复杂度为 $O(n log_2^2 n)$ 。

      1 //It is made by Awson on 2018.1.6
      2 #include <set>
      3 #include <map>
      4 #include <cmath>
      5 #include <ctime>
      6 #include <queue> 
      7 #include <stack>
      8 #include <cstdio>
      9 #include <string>
     10 #include <vector>
     11 #include <cstdlib>
     12 #include <cstring>
     13 #include <iostream>
     14 #include <algorithm>
     15 #define LL long long
     16 #define RE register
     17 #define lowbit(x) ((x)&(-(x)))
     18 #define Max(a, b) ((a) > (b) ? (a) : (b))
     19 #define Min(a, b) ((a) < (b) ? (a) : (b))
     20 #define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
     21 using namespace std;
     22 const int N = 100000;
     23 const int M = 500000;
     24 const int INF = ~0u>>1;
     25  
     26 int n, m, u, v, fa[N+5], light[N+5];
     27 char ch[5];
     28 struct tt {
     29     int to, next;
     30 }edge[(N<<1)+5];
     31 int path[N+5], top;
     32 void add(int u, int v) {
     33     edge[++top].to = v;
     34     edge[top].next = path[u];
     35     path[u] = top;
     36 }
     37 namespace LCA {
     38     int bin[25], lim, dep[N+5], fa[N+5][25];
     39     void dfs(int o, int depth, int father) {
     40         fa[o][0] = father, dep[o] = depth; 
     41         for (int i = path[o]; i; i = edge[i].next)
     42             if (edge[i].to != father) dfs(edge[i].to, depth+1, o);
     43     }
     44     int query(int x, int y) {
     45         if (dep[x] < dep[y]) Swap(x, y);
     46         for (int i = lim; i >= 0; i--) if (dep[fa[x][i]] >= dep[y]) x = fa[x][i];
     47         if (x == y) return x;
     48         for (int i = lim; i >= 0; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
     49         return fa[x][0];
     50     }
     51     int dist(int x, int y) {return dep[x]+dep[y]-(dep[query(x, y)]<<1); }
     52     void main() {
     53         lim = log(n)/log(2), bin[0] = 1; for (int i = 1; i <= 20; i++) bin[i] = bin[i-1]<<1;
     54         dfs(1, 1, 0);
     55         for (int t = 1; t <= lim; t++) for (int i = 1; i <= n; i++) fa[i][t] = fa[fa[i][t-1]][t-1];
     56     }
     57 }
     58 struct heap {
     59     priority_queue<int>q, p;
     60     void pop() {
     61         while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
     62         q.pop();
     63     }
     64     void erase(int val) {p.push(val); }
     65     int top() {
     66         while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
     67         return q.top();
     68     }
     69     int sec() {
     70         while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
     71         int tmp = q.top(); q.pop();
     72         while (!q.empty() && !p.empty() && q.top() == p.top()) q.pop(), p.pop();
     73         int val = q.top();
     74         q.push(tmp); return val;
     75     }
     76     int size() {return q.size()-p.size(); }
     77     void push(int val) {q.push(val); }
     78 }q1[N+5], q2[N+5], q3;
     79 namespace Point_divide {
     80     int size[N+5], mx[N+5], vis[N+5], root, minsize;
     81     void get_size(int o, int fa) {
     82         size[o] = 1, mx[o] = 0;
     83         for (int i = path[o]; i; i = edge[i].next)
     84             if (edge[i].to != fa && !vis[edge[i].to]) {
     85                 get_size(edge[i].to, o);
     86                 size[o] += size[edge[i].to];
     87                 if (size[edge[i].to] > mx[o]) mx[o] = size[edge[i].to];
     88             }
     89     }
     90     void get_root(int o, int pa, int fa) {
     91         mx[o] = Max(mx[o], size[pa]-size[o]);
     92         if (mx[o] < minsize) minsize = mx[o], root = o;
     93         for (int i = path[o]; i; i = edge[i].next)
     94             if (edge[i].to != fa && !vis[edge[i].to]) get_root(edge[i].to, pa, o);
     95     }
     96     void get_dist(int o, int pa, int fa) {
     97         q1[root].push(LCA::dist(pa, o));
     98         for (int i = path[o]; i; i = edge[i].next)
     99             if (edge[i].to != fa && !vis[edge[i].to]) get_dist(edge[i].to, pa, o);
    100     }
    101     void work(int o, int pa) {
    102         minsize = INF; get_size(o, 0), get_root(o, o, 0);
    103         fa[root] = pa, vis[root] = 1; q2[root].push(0), q1[root].push(LCA::dist(pa, root));
    104         for (int i = path[root]; i; i = edge[i].next)
    105             if (!vis[edge[i].to]) get_dist(edge[i].to, pa, root);
    106         q2[pa].push(q1[root].top()); int rt = root;
    107         for (int i = path[root]; i; i = edge[i].next)
    108             if (!vis[edge[i].to]) work(edge[i].to, rt);
    109         if (q2[rt].size() >= 2) q3.push(q2[rt].top()+q2[rt].sec());
    110     }
    111     void main() {work(1, 0); }
    112 }
    113 
    114 void turnon(int o) {
    115     if (q2[o].size() >= 2) q3.erase(q2[o].top()+q2[o].sec());
    116     q2[o].erase(0);
    117     if (q2[o].size() >= 2) q3.push(q2[o].top()+q2[o].sec());
    118     for (int x = o; fa[x]; x = fa[x]) {
    119         if (q2[fa[x]].size() >= 2) q3.erase(q2[fa[x]].top()+q2[fa[x]].sec());
    120         q2[fa[x]].erase(q1[x].top());
    121         q1[x].erase(LCA::dist(o, fa[x]));
    122         if (q1[x].size()) q2[fa[x]].push(q1[x].top());
    123         if (q2[fa[x]].size() >= 2) q3.push(q2[fa[x]].top()+q2[fa[x]].sec());
    124     }
    125 }
    126 void turnoff(int o) {
    127     if (q2[o].size() >= 2) q3.erase(q2[o].top()+q2[o].sec());
    128     q2[o].push(0);
    129     if (q2[o].size() >= 2) q3.push(q2[o].top()+q2[o].sec());
    130     for (int x = o; fa[x]; x = fa[x]) {
    131         if (q2[fa[x]].size() >= 2) q3.erase(q2[fa[x]].top()+q2[fa[x]].sec());
    132         if (q1[x].size()) q2[fa[x]].erase(q1[x].top());
    133         q1[x].push(LCA::dist(fa[x], o));
    134         q2[fa[x]].push(q1[x].top());
    135         if (q2[fa[x]].size() >= 2) q3.push(q2[fa[x]].top()+q2[fa[x]].sec());
    136     }
    137 }
    138 void work() {
    139     scanf("%d" ,&n);
    140     for (int i = 1; i < n; i++) {
    141         scanf("%d%d", &u, &v);
    142         add(u, v), add(v, u);
    143     }
    144     LCA::main(); Point_divide::main();
    145     int cnt = n; scanf("%d", &m);
    146     while (m--) {
    147         scanf("%s", ch);
    148         if (ch[0] == 'G') {
    149             if (cnt <= 1) printf("%d
    ", cnt-1);
    150             else printf("%d
    ", q3.top());
    151         }else {
    152             scanf("%d", &u);
    153             if (light[u]) turnoff(u), cnt++;
    154             else turnon(u), cnt--;
    155             light[u] ^= 1;
    156         }
    157     }
    158 }
    159 int main() {
    160     work();
    161     return 0;
    162 }
  • 相关阅读:
    《病理学》读书笔记
    《药理学》读书笔记
    《人格心理学》读书笔记
    《普通心理学》读书笔记
    UI进阶 跳转系统设置相关界面的方法
    安装cocoapods
    CocoaPods 添加第三方库报错
    Objective-c setObject:forKey:和setValue:forKey:的区别
    解析稍微复杂一点的数据
    获取当前屏幕显示的viewcontroller
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/8215779.html
Copyright © 2020-2023  润新知