[动态点分治] BZOJ1095: [ZJOI2007]Hide 捉迷藏

题意

给定N个节点的一棵树,一开始所有点都是黑色。需要执行Q个操作,操作有两种类型:
1.改变单点的颜色(黑变白,白变黑)。
2.询问最远黑色点对的距离。
N ≤100000, M ≤500000

题解

动态点分治经典题,一般和树上路径有关的题目都需要往这方面考虑。
对于每个点分树,我们把信息都收集到根节点上。
对于过某个根节点的路径,要求最长的一条:先对根节点的每个直接儿子求出以这个儿子为根的子树中的点到根节点的距离的最大值。在若干个最大值中挑最大的和次大的加和即是答案,我们的目标就是动态维护这些东西。
我们需要在子点分树上维护其中所有点到它父点分树根节点的距离,用一个堆h1[i]维护。
然后对每个父点分树的根节点,把它的子点分树的h1[i]的top全部收集起来放在堆里h2[i],还需要放一个0,表示根节点自己。
对于每个堆h2[i],把它的第一大和第二大的和加起来放在一个全局堆ans中。ans的堆顶即是总的答案。
注意我们只在堆中加入黑点,改变颜色操作对应的是添加/删除单点。
如何处理添加/删除呢?
因为深度是logn的,也就是说一个点最多存在于logn棵点分树中,所以直接暴力从下往上爬,途中更新一下他影响到的堆。
例如改变的点是x,当前x在的点分树的根为G, faG是父点分树的根:
1.删除h2[prt[G]]在ans中的影响。
2.删除h2[prt[G]]中对应x所在的子树的元素。
3.在h1[G]中添加/删除x。
4. 添加h2[prt[G]]中对应x所在的子树的元素。
5.添加h2[prt[G]]在ans中的影响。

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100005, maxe=200005;
int n,Q,sz[maxn],prt[maxn];
int fir[maxn],nxt[maxe],son[maxe],tot;
bool vis[maxn],sta[maxn];
//--------------------------------------------------------------------------------------
void dfs_size(int x,int fa){  
     sz[x]=1;  
     for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]) 
      dfs_size(son[j],x), sz[x]+=sz[son[j]];  
} 
int allmin,root,allsz;
void dfs_G(int x,int fa){
     int _max=0;  
     for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa&&!vis[son[j]]){
        dfs_G(son[j],x);  
        _max=max(_max,sz[son[j]]);  
     }  
     if(allmin>max(_max,allsz-sz[x])){  
        allmin=max(_max,allsz-sz[x]);  
        root=x;  
     } 
}
int Find_G(int x){
    allmin=1e+9; 
    dfs_size(x,0); allsz=sz[x];
    dfs_G(x,0);
    return root;
}
//--------------------------------------------------------------------------------------
struct Heap{
    priority_queue< int > q, del;
    void push(int x){ q.push(x); }
    void erase(int x){ del.push(x); }
    void maintain(){ while(!del.empty()&&del.top()==q.top()) del.pop(), q.pop(); }
    int top(){ maintain(); return q.top(); }  
    void pop(){ maintain(); q.pop(); }
    int sec_top(){
        int tmp=top(); pop();  
        int res=top(); push(tmp);  
        return res; 
    }  
    int size(){ return q.size()-del.size(); }
} ans,h1[maxn],h2[maxn];
void Insert_ans(Heap &x){  
     if(x.size()>1) ans.push(x.top()+x.sec_top());  
}  
void Erase_ans(Heap &x){  
     if(x.size()>1) ans.erase(x.top()+x.sec_top());  
}
//--------------------------------------------------------------------------------------
int dep[maxn],anc[maxn][25];
void dfs_LCA(int x,int fa){
    anc[x][0]=fa;
    for(int j=1;j<=20;j++) anc[x][j]=anc[anc[x][j-1]][j-1];
    for(int j=fir[x];j;j=nxt[j]) if(son[j]!=fa)
    dep[son[j]]=dep[x]+1, dfs_LCA(son[j],x);
}
int LCA(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int j=20;j>=0;j--) if(dep[anc[x][j]]>=dep[y]) x=anc[x][j]; 
    if(x==y) return x;
    for(int j=20;j>=0;j--) if(anc[x][j]!=anc[y][j]) x=anc[x][j], y=anc[y][j];
    return anc[x][0];
}
int getDis(int x,int y){
    return dep[x]+dep[y]-dep[LCA(x,y)]*2;
}
//--------------------------------------------------------------------------------------
void work(int x,int fa,int id){
     h1[id].push(getDis(x,prt[id])); 
     for(int j=fir[x];j;j=nxt[j]) 
     if(son[j]!=fa&&!vis[son[j]]) work(son[j],x,id);  
}
int DivTree(int x,int faG){  
    int G=Find_G(x); prt[G]=faG; 
    vis[G]=true;
    h2[G].push(0); 
    work(G,0,G); 
    for(int j=fir[G];j;j=nxt[j]) 
    if(!vis[son[j]]){  
        int sonG=DivTree(son[j],G);  
        if(h1[sonG].size()) h2[G].push(h1[sonG].top());    
    }  
    Insert_ans(h2[G]);  
    return G;
} 
void Updata(int x,int k){ //k=0 delete   k=1 insert 
    Erase_ans(h2[x]);
    if(!k) h2[x].erase(0);
      else h2[x].push(0);
    Insert_ans(h2[x]);
    for(int y=x;prt[y];y=prt[y]){  
       Erase_ans(h2[prt[y]]);  
       if(h1[y].size()) h2[prt[y]].erase(h1[y].top());  
       if(!k) h1[y].erase(getDis(x,prt[y])); 
         else h1[y].push(getDis(x,prt[y]));
       if(h1[y].size()) h2[prt[y]].push(h1[y].top());  
       Insert_ans(h2[prt[y]]);  
    } 
}
//--------------------------------------------------------------------------------------
void add(int x,int y){
    son[++tot]=y; nxt[tot]=fir[x]; fir[x]=tot;
}
int getint(){
    int res=0; char ch=getchar();
    while(!('0'<=ch&&ch<='9')) ch=getchar();
    while('0'<=ch&&ch<='9') res=res*10+ch-'0', ch=getchar();
    return res; 
}
int main(){
    freopen("bzoj1095.in","r",stdin);
    freopen("bzoj1095.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int x=getint(),y=getint();
        add(x,y); add(y,x);
    }
    dfs_LCA(1,0);
    DivTree(1,0);
    int cnt_now=n;
    scanf("%d",&Q);
    while(Q--){
        char ch=getchar(); while(ch!='C'&&ch!='G') ch=getchar();
        if(ch=='C'){
            int x=getint();
            Updata(x,sta[x]);
            cnt_now+=sta?1:-1; sta[x]^=1;
        }
        if(ch=='G'){
            if(cnt_now<1) printf("%d\n",cnt_now-1);
                     else printf("%d\n",ans.top()); 
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值