zoukankan      html  css  js  c++  java
  • [ZJOI2007]捉迷藏(动态点分治/(括号序列)(线段树))

    题目描述
    Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。

    游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。

    我们将以如下形式定义每一种操作:

    C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。
    G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。
    输入格式
    第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。

    接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。

    接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。

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


    这题本来老师是让用动态点分治做的,但是我用了一种括号序列的做法。

    我们可以将一个点在dfs便历时放入一个左括号,回溯的时候放入一个右括号,这样就得到了一棵树的括号序列。

    这个括号序列有一个性质,可以通过这个括号序列得到树上两点的距离。

    例如在这里插入图片描述
    这棵树的括号序列是:((3(5(8))(4(2)(6))(1(7))))

    假如要求2到7的距离,可以截取2到7这一段括号序列(2)(6))(1(7)

    删去数字和可以匹配的括号,剩下:())((),一共四个括号,距离为4。

    为什么有这个性质:

    (1^o)只添加了左括号

    这样子的话,这个点肯定是u的祖先,在路径上

    (2^o)添加了左右括号

    这样子的话,这个点肯定是祖先的其他儿子,或者是u点的儿子,产生不了贡献,所以要抵消。

    (3^o)左右括号都没添加

    这样子的话,这个点肯定是祖先的其他儿子,产生不了贡献,不用计算。

    (4^o)只添加了右括号

    这样子的话,有多少个右括号,就说明跳到lca需要多少步。

    综述,上面的性质成立。

    然后就到了维护答案的时候了。

    设一个区间有(a)个右括号,(b)个左括号,左儿子区间有(a1)个右括号,(b1)个左括号,右儿子区间有(a2)个右括号,(b2)个左括号。

    那这个大区间的答案就是(a1+|b1-a2|+b2=max(a1+b1-a2+b2,a1-b1+a2+b2)=max((a1+b1)+(b2-a2),(a1-b1)+(a2+b2)))

    显然((a1+b1),(b2-a2),(a1-b1),(a2+b2))都是可以区间单独维护的,前缀维护l1(a+b),l2(b-a),r1(a+b),r2(a-b)。

    然后就是区间合并答案了。

    (1^o)(a,b)

    (egin{cases}a=a1,b=b1-a2+b2&b1>a2\a=a1+a2-b1,b=b2&b1leqslant{a2}end{cases})

    这个很好理解吧。

    (2^o)(l1,l2,b1,b2)

    tree[hao].l1=max(tree[lc].l1,max(tree[rc].l1+tree[lc].a-tree[lc].b,tree[rc].l2+tree[lc].a+tree[lc].b));
    tree[hao].l2=max(tree[lc].l2,tree[rc].l2-tree[lc].a+tree[lc].b);
    tree[hao].r1=max(tree[rc].r1,max(tree[lc].r1-tree[rc].a+tree[rc].b,tree[lc].r2+tree[rc].a+tree[rc].b));
    tree[hao].r2=max(tree[rc].r2,tree[lc].r2+tree[rc].a-tree[rc].b);
    

    l1和l2是前缀的,所以整个区间要么是lson贡献的,要么是rson+lson贡献来的。

    r1和r2同理。

    (3^o)ans

    tree[hao].sum=max(max(tree[lc].sum,tree[rc].sum),max(tree[lc].r1+tree[rc].l2,tree[lc].r2+tree[rc].l1));
    

    直接按照推出来的式子计算即可

    剩下的就是线段树基本操作了。

    另:初始化时要(-INF)

    #include<bits/stdc++.h>
    #define lc hao<<1
    #define rc hao<<1|1
    #define inf 214748364
    #define N 100010
    using namespace std;
    struct data
    {
        int a,b,l1,l2,r1,r2,sum;//right left max(a+b) max(b-a) max(a+b) max(a-b) ans
    }tree[N<<4];
    int to[N<<1],nxt[N<<1],head[N],st[N<<2],nct,cnt,tot,val[N],n,m,x,y;
    bool is[N];
    char ch[2];
    void adde(int x,int y)
    {
        to[++nct]=y;
        nxt[nct]=head[x];
        head[x]=nct;
    }
    void dfs(int u,int fa)
    {
        st[++cnt]=-1;
        st[++cnt]=u;
        val[u]=cnt;
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(v!=fa)
            {
                dfs(v,u);
            }
        }
        st[++cnt]=-2;
    }
    void up(int hao)
    {
        if(tree[lc].b>tree[rc].a)
        {
            tree[hao].a=tree[lc].a;
            tree[hao].b=tree[lc].b-tree[rc].a+tree[rc].b;
        }else{
            tree[hao].b=tree[rc].b;
            tree[hao].a=tree[rc].a-tree[lc].b+tree[lc].a;
        }
        tree[hao].l1=max(tree[lc].l1,max(tree[rc].l1+tree[lc].a-tree[lc].b,tree[rc].l2+tree[lc].a+tree[lc].b));
        tree[hao].l2=max(tree[lc].l2,tree[rc].l2-tree[lc].a+tree[lc].b);
        tree[hao].r1=max(tree[rc].r1,max(tree[lc].r1-tree[rc].a+tree[rc].b,tree[lc].r2+tree[rc].a+tree[rc].b));
        tree[hao].r2=max(tree[rc].r2,tree[lc].r2+tree[rc].a-tree[rc].b);
        tree[hao].sum=max(max(tree[lc].sum,tree[rc].sum),max(tree[lc].r1+tree[rc].l2,tree[lc].r2+tree[rc].l1));
    }
    void change(int hao,int x)
    {
        tree[hao].a=tree[hao].b=0;
        tree[hao].l1=tree[hao].r1=tree[hao].l2=tree[hao].r2=tree[hao].sum=-inf;
        if(st[x]==-1)
        {
            tree[hao].b=1;
        }else{
            if(st[x]==-2)
            {
                tree[hao].a=1;
            }else{
                if(!is[st[x]])
                {
                    tree[hao].l1=tree[hao].r1=tree[hao].l2=tree[hao].r2=0;
                }
            }
        }
    }
    void build(int hao,int l,int r)
    {
        if(l==r)
        {
            change(hao,l);
            return;
        }
        int mid=(l+r)>>1;
        build(lc,l,mid);
        build(rc,mid+1,r);
        up(hao);
    }
    void update(int hao,int l,int r,int x)
    {
        if(l==r)
        {
            change(hao,l);
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid)
        {
            update(lc,l,mid,x);
        }else{
            update(rc,mid+1,r,x);
        }
        up(hao);
    }
    int main()
    {
    //  freopen("1.txt","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            adde(x,y);
            adde(y,x);
        }
        dfs(1,-1);
        tot=n;
        build(1,1,cnt);
        scanf("%d",&m);
        while(m--)
        {
            scanf("%s",ch);
            if(ch[0]=='G')
            {
                if(tot==1)
                {
                    puts("0");
                }else{
                    if(tot==0)
                    {
                        puts("-1");
                    }else{
                        printf("%d
    ",tree[1].sum);
                    }
                }
            }else{
                scanf("%d",&x);
                if(is[x])
                {
                    tot++;
                    is[x]=0;
                }else{
                    tot--;
                    is[x]=1;
                }
                update(1,1,cnt,val[x]);
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    [整机笔记][转贴]硬盘无法双击打开的解决办法
    [网络随摘][转载]值得用一生回味的经典语录
    [网络随摘][转载]如果你已经过了20还不到25岁
    [网络随摘][转摘]只有十句话,我却看了十分钟
    实现百台手机异步并发定时自动GPS定位打卡,基于python全天后定时签到稳定版。
    【FireFox】在Firefox中,关于隐藏table中某一行tr,其他td的边框显示异常
    【记】屏蔽浏览器shift+鼠标滚轴事件
    【记】Javascript的函数直接量定义
    【Javascript】Javascript中的函数调用模式
    【记】three.js的一个简单的代码记录
  • 原文地址:https://www.cnblogs.com/2017gdgzoi44/p/12000768.html
Copyright © 2011-2022 走看看