bzoj千题计划245:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095

查询最远点对,带修改

显然可以用动态点分治

对于每个点,维护两个堆

堆q1[x] 维护 点分树x的子树中,所有黑点到x的点分树中父节点的距离

堆q2[x]维护 点分树x的子节点的堆q1的堆顶,即若y是x在点分树中的子节点,则q2[x].push(q1[y].top())

再来维护一个全局的堆Q,维护所有q2的堆顶,即Q.push(q2[x].top())

#include<cmath>
#include<queue>
#include<cstdio>
#include<iostream>

using namespace std;

#define N 100001

#define Swap(a,b) ( (a)^=(b),(b)^=(a),(a)^=(b) )

int n;

int front[N],to[N<<1],nxt[N<<1],tot;

int fa[N];

bool light[N];
int sum;

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }
}

void add(int u,int v)
{
    to[++tot]=v; nxt[tot]=front[u]; front[u]=tot;
    to[++tot]=u; nxt[tot]=front[v]; front[v]=tot;
}

namespace LCA
{
    int fa[N][18],dep[N],bin[18],lim;

    void dfs(int x)
    {
        for(int i=front[x];i;i=nxt[i])
            if(to[i]!=fa[x][0])
            {
                dep[to[i]]=dep[x]+1;
                fa[to[i]][0]=x;
                dfs(to[i]);
            }
    }

    int query(int x,int y)
    {
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=lim;i>=0;--i)
            if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
        if(x==y) return x;
        for(int i=lim;i>=0;--i)
            if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
        return fa[x][0];
    }

    int dist(int x,int y)
    {
        return dep[x]+dep[y]-(dep[query(x,y)]<<1);
    }

    void main()
    {
        lim=log(n)/log(2);
        bin[0]=1;
        for(int i=1;i<18;++i) bin[i]=bin[i-1]<<1;
        dep[1]=1;
        dfs(1);
        for(int i=1;i<=lim;++i)
            for(int j=1;j<=n;++j)
                fa[j][i]=fa[fa[j][i-1]][i-1];
    }
}

struct HEAP
{
    priority_queue<int>heap,trash;

    void Pop(int x) { trash.push(x); }

    void Push(int x) { heap.push(x); }

    int Size() { return heap.size()-trash.size(); }

    int Top()
    {
        if(Size())
        {
            while(!trash.empty() && heap.top()==trash.top()) heap.pop(),trash.pop();
            return heap.top();
        }
        return 0;
    }

    int Top_Sec()
    {
        if(Size()>=2)
        {
            int first,second;
            while(!trash.empty() && heap.top()==trash.top()) heap.pop(),trash.pop();
            first=heap.top(); heap.pop();
            while(!trash.empty() && heap.top()==trash.top()) heap.pop(),trash.pop();
            second=heap.top(); heap.push(first);
            return first+second;
        }
        else return Top();
    }

}q1[N],q2[N],Q;

namespace Point_divide
{
    int siz[N],mx[N];
    bool vis[N];

    int root,min_size;

    void get_dist(int x,int pa,int fa)
    {
        q1[root].Push(LCA::dist(pa,x));
        for(int i=front[x];i;i=nxt[i])
            if(to[i]!=fa && !vis[to[i]]) get_dist(to[i],pa,x);
    }

    void get_size(int x,int fa)
    {
        siz[x]=1; mx[x]=0;
        for(int i=front[x];i;i=nxt[i])
            if(!vis[to[i]] && to[i]!=fa)
            {
                get_size(to[i],x);
                siz[x]+=siz[to[i]];
                if(siz[to[i]]>mx[x]) mx[x]=siz[to[i]];
            }
    }

    void get_root(int x,int pa,int fa)
    {
        mx[x]=max(mx[x],siz[pa]-siz[x]);
        if(mx[x]<min_size) min_size=mx[x],root=x;
        for(int i=front[x];i;i=nxt[i])
            if(to[i]!=fa && !vis[to[i]]) get_root(to[i],pa,x);
    }

    void work(int x,int pa)
    {
        min_size=n+1;
        get_size(x,0);
        get_root(x,x,0);
        fa[root]=pa;
        vis[root]=true;
        q2[root].Push(0);
        q1[root].Push(LCA::dist(pa,root));
        for(int i=front[root];i;i=nxt[i])
            if(!vis[to[i]]) get_dist(to[i],pa,root);
        q2[pa].Push(q1[root].Top());
        int rt=root;
        for(int i=front[root];i;i=nxt[i])
            if(!vis[to[i]]) work(to[i],rt);
        if(q2[rt].Size()>=2) Q.Push(q2[rt].Top_Sec());
    }

    void main()
    {
        work(1,0);
    }

}

void turn_off(int x)
{
    if(q2[x].Size()>=2) Q.Pop(q2[x].Top_Sec());
    q2[x].Push(0);
    if(q2[x].Size()>=2) Q.Push(q2[x].Top_Sec());
    for(int u=x;fa[u];u=fa[u])
    {
        if(q2[fa[u]].Size()>=2) Q.Pop(q2[fa[u]].Top_Sec());
        if(q1[u].Size()) q2[fa[u]].Pop(q1[u].Top());
        q1[u].Push(LCA::dist(fa[u],x));
        q2[fa[u]].Push(q1[u].Top());
        if(q2[fa[u]].Size()>=2) Q.Push(q2[fa[u]].Top_Sec());
    }
}

void turn_on(int x)
{
    if(q2[x].Size()>=2) Q.Pop(q2[x].Top_Sec());
    q2[x].Pop(0);
    if(q2[x].Size()>=2) Q.Push(q2[x].Top_Sec());
    for(int u=x;fa[u];u=fa[u])
    {
        if(q2[fa[u]].Size()>=2) Q.Pop(q2[fa[u]].Top_Sec());
        q2[fa[u]].Pop(q1[u].Top());
        q1[u].Pop(LCA::dist(x,fa[u]));
        if(q1[u].Size()) q2[fa[u]].Push(q1[u].Top());
        if(q2[fa[u]].Size()>=2) Q.Push(q2[fa[u]].Top_Sec());
    }
}

int main()
{
    //freopen("hide.in","r",stdin);
    //freopen("hide.out","w",stdout);
    read(n);
    int u,v;
    for(int i=1;i<n;++i)
    {
        read(u); read(v);
        add(u,v);
    }
    LCA::main();
    Point_divide::main();
    sum=n;
    int m; char s[3];
    read(m);
    // printf("%d\n",Q.Top());
    while(m--)
    {
        scanf("%s",s);
        if(s[0]==‘G‘)
        {
            if(sum>=2) printf("%d\n",Q.Top());
            else printf("%d\n",sum-1);
        }
        else
        {
            read(u);
            if(light[u])turn_off(u),sum++;
            else turn_on(u),sum--;
            light[u]^=1;
            //printf("%d\n",Q.Top());
        }
    }
    return 0;
}  

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8463436.html

时间: 2024-08-06 16:15:11

bzoj千题计划245:bzoj1095: [ZJOI2007]Hide 捉迷藏的相关文章

[bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治

[bzoj1095][ZJOI2007]Hide 捉迷藏 2015年4月20日7,8876 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在

bzoj千题计划252:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095 点分树+堆 请去看 http://www.cnblogs.com/TheRoadToTheGold/p/8463436.html 线段树维护括号序列 对树进行dfs,入栈时加一个左括号,出栈时加一个右括号,那么书上两点间的距离=括号序列两点间不匹配括号数 例: 树1--2--3,2为根 括号序列为 (2(3)(1)) 2和1的距离 为 ()( = 1, 3和1的距离为 )( =2 具体怎么维

bzoj千题计划163:bzoj1060: [ZJOI2007]时态同步

http://www.lydsy.com/JudgeOnline/problem.php?id=1060 以激发器所在节点为根 终止节点一定是叶节点 记录点的子树内最深的终止节点 然后从根往下使用道具 道具的使用次数为 整棵树最深的叶子节点深度-本节点子树内最深的叶子节点深度-本节点的祖先节点已使用的道具次数 #include<cstdio> #include<iostream> using namespace std; #define N 500001 int front[N],

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-