BZOJ 1095 [ZJOI2007]Hide 捉迷藏

传送门 BZOJ 1095

大致题意

有一棵树,每个点是黑色或者白色,最开始全是黑点,有两种操作。
1 Get : 输出最远两个黑点之间的距离。
2 Change(x): 改变x号点的颜色。

动态点分治大法好,具体解法如下。

1. 构建 重心树

按照静态点分治的方法,记录每一个重心 x 的上一层重心··par(x) ,显然会形成一棵树高为 log(n) 级别的树。
对于一个重心 x 维护两个大根堆f(x),g(x)
f(x) 维护 x 为的子树中黑点到par(x) 的距离
g(x) 维护 x 的子树中黑点到x 距离最大值,也就是下一层重心的 f 堆顶。

2.维护全局答案

全局答案也用一个堆Q 来维护, Q 中的 元素是每个g(x) 的最大与次大之和。

3.修改

  • 对一个点 x 的修改,只会影响重心树上的祖先节点的堆,顺着par(x) 遍历即可。
  • 对一个 x 的祖先节点 v,要从 f(v) 中 删去 dist(x,par(v))
  • f(v) 改变可能会导致 g(par(v)) 改变 所以先从 g(par(v)) 中删掉 f(v).top 修-改 完 f(v) 后在将新的 f(v).top 加入 g(par(v))
  • 类似的, g(par(v)) 的改变对全局答案 Q <script type="math/tex" id="MathJax-Element-90">Q</script> 也会有影响,处理方法和上面一样。

4.数据结构

从之前的分析可以知道,这道题中用到的堆需要支持插入和删除键值(可删除的堆),这当然可以用两个堆实现。

5. 代码

/**************************************************************
    Problem: 1095
    User: spark
    Language: C++
    Result: Accepted
    Time:15132 ms
    Memory:155180 kb
****************************************************************/

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm>
#include<queue>

using namespace std; 
const int maxn = 200000+5;
const int inf= 0x3f3f3f3f;
template<class T> 
inline void _read(T& x){ 
    char ch= getchar(); bool mark=false; 
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') mark=true; 
    for(x=0;isdigit(ch);ch= getchar()) x= x*10+ch-'0'; 
    if(mark) x=-x; 
} 
char ch[50]; 
template <class T> 
inline void _put(T x){ 
    if(!x){putchar('0');return ;} 
    if(x<0) x=-x,putchar('-'); 
    int rear=0; 
    while(x)ch[++rear]='0'+x%10,x/=10; 
    while(rear) putchar(ch[rear--]); 
} 
inline int _log(int x){int k=0;while((1<<(k+1)<=x))k++; return k;}

struct Heap{
    priority_queue<int> q,del;
    int top(){
        while(!q.empty() && !del.empty() && q.top()==del.top())
            q.pop(),del.pop();
        return q.empty()? -1:q.top();
    }
    void pop(){
        top();
        if(!q.empty()) q.pop();
    }
    int Second(){
        int x= top();
        pop();
        int ret= top(); q.push(x); 
        return ret;
    }
    void push(int x){q.push(x);}
    void Delete(int x){del.push(x);}
    int size(){return q.size()-del.size();}
}f[maxn],ans,g[maxn];

int S,e,Last[maxn],Next[maxn],To[maxn],dep[maxn],size[maxn],maxsize[maxn];
int n,qq,tot,BlackCnt,par[maxn],black[maxn];
bool del[maxn];

struct TreeGraph{
    int fa[maxn][20];
    void Addedge(int x,int y){
        To[++e] = y; Next[e]=Last[x]; Last[x]=e;
        To[++e] = x; Next[e]=Last[y]; Last[y]=e;
    }
    void DFS(int x,int father){
        size[x]=1;
        dep[x]=dep[father]+1;
        fa[x][0]= father; 
        int k = _log(dep[x]);
        for(int i=1;i<=k;i++) fa[x][i]= fa[fa[x][i-1]][i-1];
        for(int i=Last[x];i;i=Next[i]){
            int s=To[i];
            if(s==father) continue;
            DFS(s,x);
            size[x]+=size[s];
        }
    }
    int LCA(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        int i,p=dep[x]-dep[y];
        for(i=0;i<=S;i++) 
            if((p>>i)&1) x= fa[x][i];
        if(x==y) return x;
        int k = _log(dep[x]);
        for(i=S;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]-2*dep[LCA(x,y)];}
}T;

int Q[maxn],dfs_clock;
void DFS(int x,int father){
    Q[++dfs_clock] = x;
    maxsize[x]= size[x] =1;
    for(int i=Last[x];i;i=Next[i]){
        int s= To[i];
        if(s==father || del[s]) continue;
        DFS(s,x);
        size[x]+=size[s];
        maxsize[x] = max(maxsize[x],size[s]);
    }
} 

int Centroid(int x){
    dfs_clock=0;
    DFS(x,0);
    int ret=0,Min=inf;
    for(int i=1;i<=dfs_clock;i++){
        //cout<<"i: "<<i<<" Max: "<<maxsize[i]<<endl;
        maxsize[Q[i]]= max(maxsize[Q[i]],size[x]-size[Q[i]]);
        if(maxsize[Q[i]]<Min) Min=maxsize[Q[i]],ret=Q[i];
    }
    return ret;
}

void Build_Heap(int x,int father,int center){
    //cout<<"Build_Heap: "<<x<<" "<<center<<endl;
    f[center].push(T.dist(x,par[center]));
    for(int i=Last[x];i;i=Next[i])
        if(!del[To[i]] && To[i]!=father)
            Build_Heap(To[i],x,center);
}

int Divide(int x,int father){
    x= Centroid(x);
    //cout<<"Centriod: "<<x<<endl;
    del[x]= true;
    par[x]= father;
    if(father) Build_Heap(x,0,x);
    if(f[x].size()) g[father].push(f[x].top());
    tot--;
    for(int i=Last[x];i;i=Next[i])
        if(!del[To[i]])
            Divide(To[i],x);
}

void UpdateAns(Heap& h,int type){
    if(h.size()>=2){
        //cout<<" UpdateAns: "<<h.top()+h.Second()<<" "<<type<<endl;
        if(type== 1) ans.push(h.top()+h.Second());
        else ans.Delete(h.top()+h.Second());
    }
}

void Change(int x){
    if(black[x]){
        BlackCnt--;
        for(int i=x;par[i];i=par[i]){
            UpdateAns(g[par[i]],-1);
            if(f[i].size())g[par[i]].Delete(f[i].top());
            f[i].Delete(T.dist(x,par[i]));
            if(f[i].size()) g[par[i]].push(f[i].top());
            UpdateAns(g[par[i]],1);
        }
    }
    else {
        BlackCnt++;
        for(int i=x;par[i];i=par[i]){
            UpdateAns(g[par[i]],-1);
            if(f[i].size()) g[par[i]].Delete(f[i].top());
            f[i].push(T.dist(x,par[i]));
            g[par[i]].push(f[i].top());
            UpdateAns(g[par[i]],1);
        }
    }
    black[x]^=1;
}

int main(){
    int i,j,x,y;
    _read(n); 
    tot = BlackCnt = n ;
    S= _log(n);
    for(i=1;i<n;i++){
        _read(x); _read(y);
        T.Addedge(x,y);
    }
    T.DFS(1,0);
    Divide(1,0);
    for(i=1;i<=n;i++){
        UpdateAns(g[i],1);
        black[i]=1;
    }
    _read(qq);
    while(qq--){
        char cmd = getchar();
        while(!isalpha(cmd)) cmd= getchar();
        if(cmd=='G'){
            if(!BlackCnt) puts("-1");
            else if(BlackCnt==1) puts("0");
            else _put(ans.top()),putchar('\n');
        }
        else {
            _read(x); 
            Change(x);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值