[BZOJ 1095][ZJOI2007]Hide 捉迷藏

[BZOJ 1095][ZJOI2007]Hide 捉迷藏

题意

给定一棵 \(n\) 个点的树以及 \(q\) 次操作. 操作有两种, 一种是求有效点之间的最大距离, 另一种是将一个点的有效状态反转.

\(n\le 1\times 10^5,q\le 5\times 10^5\).

题解

好像老老实实写动态点分的在榜上都垫底了qaq

我们可以发现这个求两点之间最大距离非常的点分治, 为了处理修改有效点的问题, 我们需要动态点分治.

首先我们把点分树建出来, 接下来的操作对点分树进行(距离按原树算).

然后对于每个点, 我们记录两个平衡树/堆, 一个 \(len\) 表示这个子树中的所有点到根的父亲的距离, 一个 \(maxd\) 表示各个子结点的 \(len\) 中的最大值. 全局再维护一个 \(all\) 存储每个点的 \(maxd\) 中的最大值与次大值之和.

不难发现每次查询的时候取 \(all\) 中的最大值即是答案.

修改的时候距离用LCA计算, 每次先把当前点可能产生的贡献删除, 修改后再插回去.

初始化的时候有个技巧, DFS当前分治子树的时候 \(dis[root]\) 保存的是父分治子树的根到当前点的距离. 发现这刚好就是我们要算的东西, 直接insert进set就好了.

复杂度 \(O((n+q)\log^2 n)\).

参考代码

#include <bits/stdc++.h>

const int MAXV=1e5+10;
const int MAXE=2e5+10;
typedef std::multiset<int,std::greater<int>> multiset;

struct Edge{
    int from;
    int to;
    Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* topE=E;

int n;
int q;
int root;
int cursize;
int prt[MAXV];
int dis[MAXV];
int top[MAXV];
int son[MAXV];
int size[MAXV];
int pprt[MAXV];
int deep[MAXV];
bool off[MAXV];
bool vis[MAXV];
int msize[MAXV];

multiset all;
multiset len[MAXV];
multiset maxd[MAXV];

char GetCmd();
int ReadInt();
void Solve(int);
int Dis(int,int);
int LCA(int,int);
void DFS(int,int);
void Insert(int,int);
void Modify(int,bool);
void DFS(int,int,int);
void GetRoot(int,int);
int Export(const multiset&);

int main(){
    n=ReadInt();
    for(int i=1;i<n;i++){
        int a=ReadInt(),b=ReadInt();
        Insert(a,b);
        Insert(b,a);
    }
    DFS(1,0,0);
    DFS(1,1);
    cursize=n;
    msize[0]=n;
    GetRoot(1,0);
    Solve(root);
    q=ReadInt();
    while(q--){
        if(GetCmd()=='C'){
            int x=ReadInt();
            Modify(x,off[x]^=1);
        }
        else
            printf("%d\n",*all.begin());
    }
    return 0;
}

void Modify(int x,bool opt){
    if(maxd[x].size()>1)
        all.erase(all.find(Export(maxd[x])));
    if(opt)
        maxd[x].erase(maxd[x].find(0));
    else
        maxd[x].insert(0);
    if(maxd[x].size()>1)
        all.insert(Export(maxd[x]));
    for(int p=x;prt[p]!=0;p=prt[p]){
        if(maxd[prt[p]].size()>1)
            all.erase(all.find(Export(maxd[prt[p]])));
        if(!len[p].empty())
            maxd[prt[p]].erase(maxd[prt[p]].find(*len[p].begin()));
        if(opt)
            len[p].erase(len[p].find(Dis(prt[p],x)));
        else
            len[p].insert(Dis(prt[p],x));
        if(!len[p].empty())
            maxd[prt[p]].insert(*len[p].begin());
        if(maxd[prt[p]].size()>1)
            all.insert(Export(maxd[prt[p]]));
    }
}

void DFS(int root,int prt,multiset& len){
    if(dis[root]!=0)
        len.insert(dis[root]);
    dis[root]=dis[prt]+1;
    for(Edge* i=head[root];i!=NULL;i=i->next)
        if(i->to!=prt&&!vis[i->to])
            DFS(i->to,root,len);
}

void Solve(int root){
    vis[root]=true;
    if(dis[root]!=0)
        len[root].insert(dis[root]);
    dis[root]=0;
    for(Edge* i=head[root];i!=NULL;i=i->next){
        if(!vis[i->to])
            DFS(i->to,root,len[root]);
    }
    maxd[root].insert(0);
    for(Edge* i=head[root];i!=NULL;i=i->next){
        if(!vis[i->to]){
            ::root=0;
            cursize=size[i->to];
            GetRoot(i->to,root);
            int son=::root;
            prt[son]=root;
            Solve(son);
            if(!len[son].empty())
                maxd[root].insert(*len[son].begin());
        }
    }
    if(maxd[root].size()>1)
        all.insert(Export(maxd[root]));
}

void GetRoot(int root,int prt){
    size[root]=1;
    msize[root]=0;
    for(Edge* i=head[root];i!=NULL;i=i->next){
        if(!vis[i->to]&&i->to!=prt){
            GetRoot(i->to,root);
            size[root]+=size[i->to];
            msize[root]=std::max(msize[root],size[i->to]);
        }
    }
    msize[root]=std::max(msize[root],cursize-size[root]);
    if(msize[root]<msize[::root])
        ::root=root;
}

inline void Insert(int from,int to){
    topE->from=from;
    topE->to=to;
    topE->next=head[from];
    head[from]=topE++;
}

void DFS(int root,int prt,int deep){
    size[root]=1;
    pprt[root]=prt;
    ::deep[root]=deep;
    for(Edge* i=head[root];i!=NULL;i=i->next){
        if(i->to!=prt){
            DFS(i->to,root,deep+1);
            size[root]+=size[i->to];
            if(size[i->to]>size[son[root]])
                son[root]=i->to;
        }
    }
}

void DFS(int root,int top){
    ::top[root]=top;
    if(son[root])
        DFS(son[root],top);
    for(Edge* i=head[root];i!=NULL;i=i->next)
        if(i->to!=pprt[root]&&i->to!=son[root])
            DFS(i->to,i->to);
}

inline int Dis(int x,int y){
    return deep[x]+deep[y]-2*(deep[LCA(x,y)]);
}

inline int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(deep[top[x]]<deep[top[y]])
            std::swap(x,y);
        x=pprt[top[x]];
    }
    if(deep[x]>deep[y])
        std::swap(x,y);
    return x;
}

inline int ReadInt(){
    int x=0;
    register char ch=getchar();
    while(!isdigit(ch))
        ch=getchar();
    while(isdigit(ch)){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}

inline char GetCmd(){
    register char ch=getchar();
    while(ch!='C'&&ch!='G')
        ch=getchar();
    return ch;
}

inline int Export(const multiset& s){
    auto it=s.begin();
    ++it;
    return *s.begin()+*it;
}

[BZOJ 1095][ZJOI2007]Hide 捉迷藏

上一篇:51nod 1925 + 51nod 1095


下一篇:1095 解码PAT准考证 (25 分) python