zoukankan      html  css  js  c++  java
  • 【BZOJ1095】 Hide 捉迷藏

     

    Time Limit: 4000 ms   Memory Limit: 256 MB

    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。


    Solution

      题意是有一棵黑白点树,要支持两种操作:将一个点黑白反色,或者查询任意两个黑点的距离的最大值。

      用动态树分治来做:先试着在每一个重心上,维护经过这个重心的两个黑点距离最大值。

      看看能不能维护重心$u$的管辖范围内每个黑点到$u$的距离最大值和次大值,然后加起来就是答案。用一个堆就可以实现,但是发现不好处理两个黑点处在同一子树的问题。

      如果我们能把子树的信息相互独立就好:对于$u$的每一个子树$v$,将$v$子树内所有点到$u$的距离放进一个堆$C_v$里(为了方便,将$C_v$记录在$v$子树的重心$v'$上)。那么对于$u$的每一个$v$子树,将其$C_v$的最大值再组成一个堆$B_u$。因为所有值都互相独立,所以能保证不会出现同一子树情况,因此$B_u$的最大值加上次大值就是经过这个重心的答案。

      对于所有的重心$u$,将$B_u$的最大值加上次大值,扔进一个全局堆$A$中,每次查询输出堆顶即可(如果出现题目描述特殊情况直接输出0或-1)。

      

      进行修改操作时,在点分树上向上迭代。若当前重心为$u$,其父亲为$f$,先用$B_f$的最大值加次大值来删$A$,再用$C_u$的堆顶删$B_f$,更新$C_u$,然后用$C_u$的堆顶更新$B_f$,最后用$B_f$的最大值加次大值更新$A$。  

    可按权值删除的堆:

      这道题中所使用的堆,除了加入一个权值之外,有可能要求从堆中删除一个值为$x$的元素。

      可以自己实现一个可删堆:其中包含一个主堆$a$和删堆$b$。

      增加元素时推入$a$;删除值为$x$的元素时,将$x$推入$b$;进行提取操作(pop();top();)时,如果$a$和$b$堆顶相同则弹出,直到$a$和$b$堆顶不同或$b$为空为止。这样就巧妙地实现了可删堆。

    Tips:

      1. 如果一个重心$u$自己是黑点,那么应该在$B_u$中添加一个值为0的元素(这适用于B堆将两条连拼接起来的本质);否则$B_u$内不应有值为$0$的元素。

      2. 如果一个重心$u$的$B_u$的集合大小小于2,这意味着$u$子树内只用一个黑点。那么不要用$B_u$更新$A$,$A$中不应该存在$B_u$对应的元素,因为此时根本不能组成路径。

      3. 一定要注意优化!改变一个点的状态时,用可用的条件先判断,避免过多的堆操作(真坑爹)。如新加入$C$的值,可与$C$原先堆顶的大小进行判断,如果比原堆顶大,再更新$B$和$A$,否则可以不用进行接下来的操作;删除$C$中的一个值,先判断该值是否和$C$的堆顶相同,如果是才更新$B$和$A$,否则不用操作。


    #include <cstdio>
    #include <queue>
    using namespace std;
    const int N=100005,Bas=19,INF=2147000000;
    int n,q,h[N],tot,cnt;
    int sum,nrt,nval,size[N],cut[N];
    int dep[N],pre[N][Bas];
    int fa[N];
    int st[N];
    struct Edge{int v,next;}g[N*2];
    struct Heap{
        priority_queue<int> a,b;
        int size(){return a.size()-b.size();}
        void push(int x){if(x!=-1) a.push(x);}
        void erase(int x){if(x!=-1) b.push(x);}
        void pop(){
            while(b.size()&&a.top()==b.top()) a.pop(),b.pop();
            a.pop();
        }
        int top(){
            while(b.size()&&a.top()==b.top()) a.pop(),b.pop();
            return a.size()?a.top():-1;
        }
        int sop(){
            if(size()<2) return 0;
            int x=top(); pop();
            int y=top(); push(x);
            return y;
        }
        int count(){
            int sz=size(),x=top(),y=sop();
            if(sz<2) return -1;
            else return x+y;
        }
    }a,b[N],c[N];
    inline int rd(){
        char c;
        int x=0;
        while((c=getchar())<'0'||c>'9');
        x=c-'0';
        while('0'<=(c=getchar())&&c<='9') x=x*10+c-'0';
        return x;
    }
    inline void swap(int &x,int &y){int t=x;x=y;y=t;}
    inline void addEdge(int u,int v){
        g[++tot].v=v; g[tot].next=h[u]; h[u]=tot;
        g[++tot].v=u; g[tot].next=h[v]; h[v]=tot;
    }
    void predfs(int u,int fa,int Dep){
        dep[u]=Dep;
        pre[u][0]=fa;
        for(int i=1;i<Bas;i++) pre[u][i]=pre[pre[u][i-1]][i-1];
        for(int i=h[u],v;i;i=g[i].next)
            if((v=g[i].v)!=fa)
                predfs(v,u,Dep+1);
    }
    int getlca(int a,int b){
        if(dep[a]<dep[b]) swap(a,b);
        for(int i=Bas-1;i>=0;i--)
            if(dep[pre[a][i]]>=dep[b]) a=pre[a][i];
        if(a==b) return a;
        for(int i=Bas-1;i>=0;i--)
            if(pre[a][i]!=pre[b][i]) a=pre[a][i],b=pre[b][i];
        return pre[a][0];
    }
    int getdis(int x,int y){return (dep[x]-1)+(dep[y]-1)-2*(dep[getlca(x,y)]-1);}
    void find(int u,int fa){
        int maxs=0;
        size[u]=1;
        for(int i=h[u],v;i;i=g[i].next)
            if(!cut[v=g[i].v]&&v!=fa){
                find(v,u);
                size[u]+=size[v];
                if(size[v]>maxs) maxs=size[v];
            }
        if(sum-size[u]>maxs) maxs=sum-size[u];
        if(maxs<nval) nrt=u,nval=maxs;
    }
    void dfs(int u,int fa,int dis,Heap &heap){
        heap.push(dis);
        for(int i=h[u],v;i;i=g[i].next)
            if(!cut[v=g[i].v]&&v!=fa)
                dfs(v,u,dis+1,heap);
    }
    void solve(int u,int Fa){
        cut[u]=1; fa[u]=Fa;
        for(int i=h[u],v;i;i=g[i].next)
            if(!cut[v=g[i].v]){
                nval=INF; nrt=0; sum=size[u];        
                find(v,0);
                solve(nrt,u);
            }
    }
    void change(int u,int v,int flag){
        if(u==v){
            if(flag){
                b[u].push(0);
                if(b[u].size()==2) a.push(b[u].top());
            }    
            else{
                if(b[u].size()==2) a.erase(b[u].top());
                b[u].erase(0);
            }
        }
        if(!fa[u]) return;
        int f=fa[u],dis=getdis(f,v),last=c[u].top();
        if(flag) c[u].push(dis); else c[u].erase(dis);
        if((flag&&dis>last)||(!flag&&dis==last)){
            a.erase(b[f].count());
            b[f].erase(last);
            if(flag) b[f].push(dis);
            else b[f].push(c[u].top());
            a.push(b[f].count());
        }
        change(f,v,flag);
    }
    int main(){
        n=rd();
        for(int i=1,u,v;i<n;i++)
            u=rd(),v=rd(),addEdge(u,v);
        predfs(1,0,1);        
        nval=INF; nrt=0; sum=n;
        find(1,0);
        solve(nrt,0);
        for(int i=1;i<=n;i++){
            change(i,i,!st[i]);
            st[i]^=1;
            cnt++;
        }
        char opt[2];
        int x;
        q=rd();
        while(q--){
            scanf("%s",opt);    
            if(opt[0]=='G'){
                if(cnt==1) puts("0");
                else if(cnt==0) puts("-1");
                else printf("%d
    ",a.top());
            }
            else{
                x=rd();
                change(x,x,!st[x]);
                st[x]^=1;
                if(st[x]) cnt++; else cnt--;
            }
        }
        return 0;
    }
    奇妙代码
  • 相关阅读:
    leetcode701. Insert into a Binary Search Tree
    leetcode 958. Check Completeness of a Binary Tree 判断是否是完全二叉树 、222. Count Complete Tree Nodes
    leetcode 110. Balanced Binary Tree
    leetcode 104. Maximum Depth of Binary Tree 111. Minimum Depth of Binary Tree
    二叉树
    leetcode 124. Binary Tree Maximum Path Sum 、543. Diameter of Binary Tree(直径)
    5. Longest Palindromic Substring
    128. Longest Consecutive Sequence
    Mac OS下Android Studio的Java not found问题,androidfound
    安卓 AsyncHttpClient
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/8045759.html
Copyright © 2011-2022 走看看