Qtree4——动态点分治

题目描述

给出一棵边带权的节点数量为n的树,初始树上所有节点都是白色。有两种操作:

C x,改变节点x的颜色,即白变黑,黑变白

A,询问树中最远的两个白色节点的距离,这两个白色节点可以重合(此时距离为0)。

N (N <= 100000) Q <= 200000

时限1s

题解

如果没有修改的话,直接点分治,记录子树最深的白点即可。

但是有修改。

发现,点分治的递归层数是O(logn)的

而且这个递归的联通块的根形成一个树形结构。不妨叫点分树。

我们的答案是在所有递归出来的块里ans取max

发现,每次改变一个点的颜色,会影响自己的联通块,以及点分树上这个点的所有father的答案。

树高logn

所以,我们考虑暴力修改每一层的答案。

用三个堆来维护。

一个堆h0,维护这个点P所代表的点分树的联通块中所有点到点分树上P的father的距离(树上实际距离)。

(好处是,修改的时候,直接自底向上,父亲只要一个,这样不需要在上层再考虑哪个子树变了)

另一个堆h1,维护这个点P的所有儿子的堆顶的值。

我们从一个点P的h1堆里面,找到最大的和次大的,做和就是这一层的最大答案。

如果P是白点,那么答案可以只要最大的。而且ans最少是0

第三个堆,维护所有点代表的联通块的ans。最终答案就在这里。

然后,所有的修改,都是删除之后再插入。

堆的删除,用懒惰堆即可。

代码:

(实现细节较多:例如堆的empty判断)

(堆中不用记录答案出自哪里,随便删除一个,剩下那个就是没有删除的。是没有区别的。直接int的堆即可)

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define reg register int
#define il inline
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
il void prin(int x){
    if(x/10) prin(x/10);
    putchar(x%10+'0');
}
namespace Miracle{
const int N=100000+5;
const int inf=0x3f3f3f3f;
int n,m;
struct node{
    int nxt,to;
    int val;
}e[2*N];
int hd[N],cnt;
il void add(int x,int y,int z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].val=z;
    hd[x]=cnt;
}
int fa[N];
int sz[N];
int c[N];
priority_queue<int>h[2][N],d[2][N],hh,dd;

int dis[N][20];
int dep[N];
int nowsz;
int rt,mxsz[N];
int ans[N];
bool vis[N];
int gen;
il void dfs1(int x,int ff,int d){
    dep[x]=d;
    sz[x]=1;
    mxsz[x]=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==ff) continue;
        if(vis[y]) continue;
        dfs1(y,x,d);
        sz[x]+=sz[y];
        mxsz[x]=max(mxsz[x],sz[y]);
    }
    mxsz[x]=max(mxsz[x],nowsz-sz[x]);
    if(mxsz[x]<=nowsz/2){
        rt=x;
    }
}
il void dfs2(int x,int ff,int d){
    sz[x]=1;
    if(d!=1) h[0][rt].push(dis[x][d-1]);
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(vis[y]) continue;
        if(y==ff) continue;
        dis[y][d]=dis[x][d]+e[i].val;
        dfs2(y,x,d);
        sz[x]+=sz[y];
    }
}
il void clear(int x,int k){
    if(h[k][x].empty()) return;
    while(d[k][x].size()&&h[k][x].size()&&h[k][x].top()==d[k][x].top()){
        h[k][x].pop();d[k][x].pop();
    }
}
il void upda(int x){
    //cout<<" updaing "<<x<<endl;
    ans[x]=-inf;
    if(c[x]==0) ans[x]=max(ans[x],0);
    clear(x,1);
    if(!h[1][x].empty()){
        //cout<<" sz "<<h[1][x].size()<<" "<<d[1][x].size()<<endl;
        int tmp=h[1][x].top();h[1][x].pop();
        //cout<<" tmp "<<tmp.id<<" "<<tmp.val<<endl;
        if(c[x]==0){
            ans[x]=max(ans[x],tmp);
        }
        clear(x,1);
        if(!h[1][x].empty()){
            ans[x]=max(ans[x],tmp+(h[1][x].top()));
        }
        h[1][x].push(tmp);
    }
}
il int divi(int x,int d,int ff){
    //cout<<" x d ff "<<x<<" "<<d<<" "<<ff<<endl;
    rt=0;
    dfs1(x,0,d);
    fa[rt]=ff;
    dis[rt][d]=0;
    dfs2(rt,0,d);
    
    vis[rt]=1;
    ans[rt]=-inf;
    int now=rt;
    for(reg i=hd[now];i;i=e[i].nxt){
        int y=e[i].to;
        if(vis[y]) continue;
        nowsz=sz[y];
        int son=divi(y,d+1,now);
        
        if(h[0][son].size()){
            h[1][now].push(h[0][son].top());
        }
    }
    upda(now);
    //cout<<" rt "<<now<<" : "<<ans[now]<<endl;
    hh.push(ans[now]);
    return now;
}
il void wrk(int x){
    int gg=x;
    int nd=dep[x];
    if(c[x]==0){
        c[x]^=1;
        while(x){
            //cout<<" xx nd "<<x<<" "<<nd<<" "<<endl;
            dd.push(ans[x]);
            clear(x,1);
            upda(x);
            hh.push(ans[x]);
            
            if(fa[x]){
                if(h[0][x].size()) d[1][fa[x]].push(h[0][x].top());
                d[0][x].push(dis[gg][nd-1]);
                clear(x,0);
                if(h[0][x].size()) h[1][fa[x]].push(h[0][x].top());
            }
            
            x=fa[x];
            --nd;
        }
    }
    else{
        c[x]^=1;
        while(x){
            dd.push(ans[x]);
            clear(x,1);
            upda(x);
            hh.push(ans[x]);
            
            if(fa[x]){
                if(h[0][x].size()) d[1][fa[x]].push(h[0][x].top());
                h[0][x].push(dis[gg][nd-1]);
                clear(x,0);
                if(h[0][x].size()) h[1][fa[x]].push(h[0][x].top());
            }
            
            x=fa[x];
            --nd;
        }
    }
}
int main(){
    scanf("%d",&n);int x,y,z;
    for(reg i=1;i<=n-1;++i){
        rd(x);rd(y);rd(z);
        add(x,y,z);add(y,x,z);
    }
    nowsz=n;
    gen=divi(1,1,0);
    int m;
    rd(m);
    char ch[10];
    while(m--){
        scanf("%s",ch+1);
        if(ch[1]=='A'){
            
            while(dd.size()&&hh.size()&&hh.top()==dd.top()){
                hh.pop();dd.pop();
            }
            if(!hh.size()){
                puts("They have disappeared.");
            }
            else{
                int tmp=hh.top();
                if(tmp==-inf) puts("They have disappeared.");
                else {
                    (tmp<0)&&(tmp=-tmp,putchar('-'));
                    prin(tmp);putchar('
');
                }
            }
        }else{
            rd(x);
            wrk(x);
        }
//        cout<<" ans------- "<<endl;
//        for(reg i=1;i<=n;++i){
//            cout<<i<<" : "<<ans[i]<<endl;
//        }
    }
    return 0;
}

}
int main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/11/28 9:20:15
*/

你也可以顺带AC[ZJOI2007]捉迷藏

原文地址:https://www.cnblogs.com/Miracevin/p/10036123.html