ZJOI 2007 Hide 捉迷藏 题解

题目传送门

题目大意: 有一个 n n n 个点的树,每个节点有黑白两种颜色之一,一开始全是白点,现在有两种操作。操作一:修改某个点的颜色;操作二,询问最远的两个白点的距离。

题解

因为跟树上的点对距离有关系,很自然的就想到树上点分治,但是有修改操作,于是使用动态点分治即可。

那我们考虑对于每一个重心如何更新答案。

对于每一个重心,它能产生的贡献,就是子树中离自己最远的两个白点的距离之和,并且要求这两个白点来自该重心的两个不同的子树中。

那么因为有修改操作,于是我们需要用一个堆来维护这个重心的每一棵子树提供的那个最远的白点,然后取这个堆中的最大值和次大值相加便可以更新答案。

那么显然,我们还需要一个堆,去维护每一棵子树中每一个白点到重心的距离。

最后还需要一个堆,将每一个重心求出的答案进行维护,最后取堆顶便是答案。

代码嘛……自己都觉得丑,不愿意看的还是别看了,,

我尽量把注释写明白点= =

#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
#define maxn 100010
 
int n,m;
struct node{int y,next,utb;};
node e[2*maxn];
int first[maxn];
inline void buildroad(int x,int y)
{
    static int len=0;
    e[++len]=(node){y,first[x],0};
    first[x]=len;
}
vector< map<int,int> > pos[maxn];//pos[i][j][k]表示k在dui1[i][j]中的位置
int bianhao[maxn],tt=0;//bianhao[i]表示i在duians中的位置
map<int,int> position[maxn];//position[i][j]表示j在dui2[i]中的位置
struct heapp{//堆
    vector<node> a;
    int l;
    heapp(){a.clear();a.push_back((node){0,0,0});l=0;}
    void up(int x)
    {
        while(x>1&&a[x].y>a[x/2].y)
        {
            swap(bianhao[a[x].next],bianhao[a[x/2].next]);
            swap(a[x],a[x/2]);
            x/=2;
        }
    }
    void up(int x,int y)
    {
        while(x>1&&a[x].y>a[x/2].y)
        {
            swap(position[y][a[x].next],position[y][a[x/2].next]);
            swap(a[x],a[x/2]);
            x/=2;
        }
    }
    void up(int x,int tr,int num)
    {
        while(x>1&&a[x].y>a[x/2].y)
        {
            swap(pos[tr][num][a[x].next],pos[tr][num][a[x/2].next]);
            swap(a[x],a[x/2]);
            x/=2;
        }
    }
    void add(int x,int y)
    {
        a.push_back((node){x,y});
        l++;
        bianhao[y]=l;
        up(l);
    }
    void add(int x,int y,int num)
    {
        a.push_back((node){x,y});
        l++;
        position[num][y]=l;
        up(l,num);
    }
    void add(int x,int y,int tr,int num)
    {
        a.push_back((node){x,y,x});//这里的第三个参数下面有用
        l++;
        pos[tr][num][y]=l;
        up(l,tr,num);
    }
    void down(int x)
    {
        if(x>l/2)return;
        int ans=x;
        if(a[ans].y<a[x*2].y)ans=x*2;
        if(x*2+1<=l&&a[ans].y<a[x*2+1].y)ans=x*2+1;
        if(ans!=x)
        {
            swap(a[x],a[ans]);
            swap(bianhao[a[x].next],bianhao[a[ans].next]);
            down(ans);
        }
    }
    void down(int x,int num)
    {
        if(x>l/2)return;
        int ans=x;
        if(a[ans].y<a[x*2].y)ans=x*2;
        if(x*2+1<=l&&a[ans].y<a[x*2+1].y)ans=x*2+1;
        if(ans!=x)
        {
            swap(a[x],a[ans]);
            swap(position[num][a[x].next],position[num][a[ans].next]);
            down(ans,num);
        }
    }
    void down(int x,int tr,int num)
    {
        if(x>l/2)return;
        int ans=x;
        if(a[ans].y<a[x*2].y)ans=x*2;
        if(x*2+1<=l&&a[ans].y<a[x*2+1].y)ans=x*2+1;
        if(ans!=x)
        {
            swap(a[x],a[ans]);
            swap(pos[tr][num][a[x].next],pos[tr][num][a[ans].next]);
            down(ans,tr,num);
        }
    }
}dui2[maxn],duians;
vector<heapp> dui1[maxn];
//dui1[i][j]是一个堆,用来维护 以i的第j个儿子 为根的子树内每个点到 i的第j个儿子的距离
//dui2[i]也是一个堆,用来维护i的子树们提供的最远白点,也就是用dui1[i][j]的堆顶来造一个堆
//duians还是一个堆,用来维护每一个重心提供的答案,duians的堆顶就是答案
bool v[maxn];//v[i]表示i这个点是否被分治过,建完树之后用来标记这个点的颜色,true为白色,false为黑色
int son[maxn],mson[maxn],root,size;
int tot[maxn];//表示x这个重心的儿子数
map<int,int> s[maxn];//s[x][y]表示y是x的第几个儿子
void getroot(int x,int fa)//寻找重心
{
    son[x]=1;mson[x]=0;
    for(int i=first[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(v[y]||y==fa)continue;
        getroot(y,x);
        son[x]+=son[y];
        if(son[y]>mson[x])mson[x]=son[y];
    }
    if(size-son[x]>mson[x])mson[x]=size-son[x];
    if(mson[x]<mson[root])root=x;
}
int dis[maxn];
void getdis_bt(int x,int fa,int tr,int num)//得到每个点的距离,构建dui1
{
    dui1[tr][num].add(dis[x],x,tr,num);
    for(int i=first[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(v[y]||y==fa)continue;
        dis[y]=dis[x]+1;
        getdis_bt(y,x,tr,num);
    }
}
int fa[maxn],con[maxn];
inline int work(int x,int y,int z){return y>z?x+y:x+z;}
heapp wan;
map<int,int>won;
void buildtree(int x,int ssize)
{
    v[x]=true;
    for(int i=first[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(v[y])continue;
        s[x][y]=++tot[x];
        dui1[x].push_back(wan);
        pos[x].push_back(won);
        dis[y]=0;
        getdis_bt(y,0,x,s[x][y]);
        dui2[x].add(dui1[x][s[x][y]].a[1].y+1,s[x][y],x);
        root=0;
        size=son[y]<son[x]?son[y]:(ssize-son[x]);
        getroot(y,x);
        fa[root]=x;con[root]=y;
        buildtree(root,(son[y]<son[x]?son[y]:(ssize-son[x])));
    }
    if(dui2[x].l==0)duians.add(0,x);
    else if(dui2[x].l==1)duians.add(dui2[x].a[1].y,x);
    else if(dui2[x].l==2)duians.add(dui2[x].a[1].y+dui2[x].a[2].y,x);
    else if(dui2[x].l>2)duians.add(work(dui2[x].a[1].y,dui2[x].a[2].y,dui2[x].a[3].y),x);
}
int total;
void go(int x,int y,int point)
{
    if(!v[point])dui1[x][s[x][y]].a[pos[x][s[x][y]][point]].y=-23333333;
    else dui1[x][s[x][y]].a[pos[x][s[x][y]][point]].y=dui1[x][s[x][y]].a[pos[x][s[x][y]][point]].utb;//修改dui1的值
    dui1[x][s[x][y]].up(pos[x][s[x][y]][point],x,s[x][y]);
    dui1[x][s[x][y]].down(pos[x][s[x][y]][point],x,s[x][y]);//up、down一下维护堆的平衡
     
    dui2[x].a[position[x][s[x][y]]].y=max(dui1[x][s[x][y]].a[1].y+1,0);
    dui2[x].up(position[x][s[x][y]],x);dui2[x].down(position[x][s[x][y]],x);
    //dui2同理
    
    if(dui2[x].a[1].y>0&&(dui2[x].l==1||(dui2[x].l==2&&dui2[x].a[2].y==0)||(dui2[x].l>2&&dui2[x].a[2].y==0&&dui2[x].a[3].y==0))&&!v[x])duians.a[bianhao[x]].y=0;
    //下面对duians的修改与buildtree函数中的基本相同,解释一下上面这一大段
    //如果一个重心x,它的儿子们只能提供一个最远的白点(其他本来能提供的那些子树由于变成黑色无法提供),并且x的颜色是黑色时,
    //x这个重心是不能对答案做出任何贡献的,于是将他的值标成0
    else if(dui2[x].l==1)duians.a[bianhao[x]].y=dui2[x].a[1].y;
    else if(dui2[x].l==2)duians.a[bianhao[x]].y=dui2[x].a[1].y+dui2[x].a[2].y;
    else duians.a[bianhao[x]].y=work(dui2[x].a[1].y,dui2[x].a[2].y,dui2[x].a[3].y);
    duians.up(bianhao[x]);duians.down(bianhao[x]);
     
    if(fa[x]!=0)go(fa[x],con[x],point);
}
void read(int &x)
{
    x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
}
 
int main()
{
    read(n);
    total=n;
    for(int i=1;i<n;i++)
    {
        int x,y;
        read(x);read(y);
        buildroad(x,y);
        buildroad(y,x);
    }
    root=0;mson[root]=999999999;
    for(int i=1;i<=n;i++)
    dui1[i].push_back(wan),pos[i].push_back(won);//填充vector的0位置
    size=n;
    getroot(1,0);
    buildtree(root,n);
    read(m);
    for(int i=1;i<=m;i++)
    {
        char ss[3];
        scanf("%s",ss);
        if(ss[0]=='G')
            if(total==0)printf("-1\n");
            else if(total==1)printf("0\n");
            else printf("%d\n",duians.a[1].y);
        else
        {
            int x;
            read(x);
            if(v[x])v[x]=false,total--;
            else v[x]=true,total++;
            //下面与go函数中的同理
            if(dui2[x].a[1].y>0&&(dui2[x].l==1||(dui2[x].l==2&&dui2[x].a[2].y==0)||(dui2[x].l>2&&dui2[x].a[2].y==0&&dui2[x].a[3].y==0)))
            {
                if(v[x])duians.a[bianhao[x]].y=0,duians.down(bianhao[x]);
                else duians.a[bianhao[x]].y=dui2[x].a[1].y,duians.up(bianhao[x]);
            }
            if(fa[x]!=0)go(fa[x],con[x],x);
        }
    }
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值