[BZOJ1095][ZJOI2007][线段树]Hide捉迷藏

[Problem Description]
捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。
[Algorithm]
线段树
[Analysis]
这道题本来应该是用高级数据结构做的……但是很显然我不会……后来看到网上的题解,学到了一种非常高级的方法……通过模型转化然后用线段树求解。
我们先通过DFS将这棵树转换成一个序列。每一棵子树我们都用一对()括起来。例如树 2 - 1 - 3(root = 1)的序列为: (1(2)(3))
这样我们就会发现,两个点之间的距离就是它们在序列上的点之间不匹配的括号的数量(即把中间匹配的括号去掉)
这样问题就变成了求序列中两个不亮灯的点之间最长的不匹配括号的数量。由于涉及点修改,总查询,所以用线段树维护。
现在有一个问题:如何合并两个区间?
假设用S(a, b)表示S中左括号和右括号的数量,Dis(S) = a + b, 合并S1(a1, b1), S2(a2, b2),显然有
Dis(S) = a1 + b2 + |b1 - a2|
变化一下得 Dis(S) = max[(a1 + b1) + (b2 - a2), (a1 - b1) + (a2 + b2)]
这样我们就需要对每个节点维护以下几个值:
a:整个区间去掉匹配后")"的个数
b:整个区间去掉匹配后"("的个数
dis:区间最优
left_plus:靠左且右端为黑灯节点,max(a + b)
left_minus:靠左且右端为黑灯节点,max(b - a)
right_plus:靠右且左端为黑灯节点,max(a + b)
right_minus:靠右且左端为黑灯节点,max(a - b)
所以:
Dis(S) = max[Dis(S1), Dis(S2), right_plus(S1) + left_minus(S2), right_minus(S1) + left_plus(S2)]
至于left_plus, left_minus等的合并就不多说了,详见程序
[Code]
/**************************************************************
    Problem: 1095
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:2468 ms
    Memory:40752 kb
****************************************************************/
 
#include 
#include 
#include 
#include 
#include 
using namespace std;
 
const int MAXN    = 100100;
const int MAXNODE = 300100;
const int INF     = 1000000000;
 
struct Node
{
    int left_plus, right_plus, left_minus, right_minus;
    int dis;
    int a, b;
};
Node seg[MAXNODE * 4];
char xulie[MAXNODE];
int totxulie = 0;
int point[MAXNODE] = {0}, next[MAXNODE] = {0}, v[MAXNODE], tot = 0;
int duiying[MAXN], n, q;
 
inline void addedge(int x, int y)
{
    tot++;
    next[tot] = point[x];
    point[x] = tot;
    v[tot] = y;
}
 
void dfs(int now, int father)
{
    xulie[++totxulie] = '(';
    xulie[++totxulie] = 'B';
    duiying[now] = totxulie;
    for (int temp = point[now]; temp; temp = next[temp])
        if (v[temp] != father)
            dfs(v[temp], now);
    xulie[++totxulie] = ')';
}
 
inline void solveSingle(int now, int x)
{
    if (xulie[x] == 'B')
    {
        seg[now].left_plus = 0;
        seg[now].right_plus = 0;
        seg[now].left_minus = 0;
        seg[now].right_minus = 0;
        seg[now].dis = 0;
        seg[now].a = 0;
        seg[now].b = 0;
    }
    else
    {
        seg[now].left_plus = -INF;
        seg[now].right_plus = -INF;
        seg[now].left_minus = -INF;
        seg[now].right_minus = -INF;
        seg[now].dis = -INF;
        seg[now].a = 0;
        seg[now].b = 0;
        if (xulie[x] == ')')
            seg[now].a = 1;
        if (xulie[x] == '(')
            seg[now].b = 1;
    }
}
 
inline void update(int now)
{
    int l = now << 1;
    int r = now << 1 | 1;
    seg[now].dis = max(max(seg[l].dis, seg[r].dis),
                       max(seg[l].right_plus + seg[r].left_minus,
                           seg[l].right_minus + seg[r].left_plus));
    if (seg[r].a > seg[l].b)
    {
        seg[now].a = seg[l].a + seg[r].a - seg[l].b;
        seg[now].b = seg[r].b;
    }
    else
    {
        seg[now].a = seg[l].a;
        seg[now].b = seg[l].b - seg[r].a + seg[r].b;
    }
    seg[now].left_plus = max(seg[l].left_plus,
                             max(seg[r].left_plus + seg[l].a - seg[l].b,
                                 seg[r].left_minus + seg[l].a + seg[l].b));
    seg[now].left_minus = max(seg[l].left_minus, seg[r].left_minus - seg[l].a + seg[l].b);
    seg[now].right_plus = max(seg[r].right_plus,
                              max(seg[l].right_plus + seg[r].b - seg[r].a,
                                  seg[l].right_minus + seg[r].a + seg[r].b));
    seg[now].right_minus = max(seg[r].right_minus, seg[l].right_minus + seg[r].a - seg[r].b);
}
 
void build(int now, int left, int right)
{
    if (left == right)
    {
        solveSingle(now, left);
        return;
    }
    int mid = (left + right) >> 1;
    build(now << 1, left, mid);
    build(now << 1 | 1, mid + 1, right);
    update(now);
}
 
void change(int now, int left, int right, int x)
{
    if (left == right)
    {
        if (xulie[x] == 'B') xulie[x] = 'W';
        else xulie[x] = 'B'; 
        solveSingle(now, left);
        return;
    }
    int mid = (left + right) >> 1;
    if (x <= mid) change(now << 1, left, mid, x);
    else change(now << 1 | 1, mid + 1, right, x);
    update(now);
}
 
int main()
{
    scanf("%d", &n);
    for (int i = 1; i < n; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        addedge(x, y);
        addedge(y, x);
    }
    dfs(1, 0);
    build(1, 1, totxulie);
    scanf("%d", &q);
    for (int i = 1; i <= q; i++)
    {
        char order;
        int x;
        scanf(" %c", &order);
        if (order == 'C')
        {
            scanf("%d", &x);
            change(1, 1, totxulie, duiying[x]);
        }
        else
            printf("%d\n", seg[1].dis);
    }
}


你可能感兴趣的:(题解)