【LuoguP2056】捉迷藏(动态点分治)

题目链接

题意

带修改询问树上最远黑色点对

Sol

会动点分的人,告诉你维护每个点的点分树上的各个子树最大点距的堆,每次暴力弹出两个统计到全局答案的堆然后你就会做了

除了修改和询问都是板子

唯一易错点:

每一个点向上更新父亲的表示子树内最大点距的堆时要先把原来的删掉,不然一颗子树内加了两个点就上去又下来了!!

代码(用其他题的代码改的,有奇怪的地方不要在意):

#include
#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
const int N=1e5+10;
bool city[N];
int num;int n,m;
namespace Tree{
    struct edge{
        int to,next,w;
    }a[N<<1];
    int head[N];
    int fa[N];int cnt=0;
    void add(int x,int y,int w){a[++cnt]=(edge){y,head[x],w};head[x]=cnt;}
    void find_root(int,int);
    void build(int,int);
    void dfs2(int,int);
    void Pre();
    void solve();
    void Build(){
        n=read();num=n;register int u,v,w;
        for(register int i=1;i1;
            add(u,v,w);add(v,u,w);
        }
        m=read();
        return;
    }
    int SZ;int f[N];int size[N];int rt;
    const int INF=1e8;
    struct heap{
        priority_queue<int> Q;priority_queue<int> P;
        void clear(){while(!Q.empty())Q.pop();while(!P.empty())P.pop();return;}
        inline int Get(){
            while((!P.empty())&&(!Q.empty())&&Q.top()==P.top()) P.pop(),Q.pop();
            return Q.empty()? -INF:Q.top();
        }
        inline void pop(){if(!Q.empty()) Q.pop();return;}
        inline void push(int x){Q.push(x);return;}
        inline void Del(int x){P.push(x);return;}
    }A[N],B[N],ANS;
    int dis[N],log[N<<1];int I=0;int dep[N];bool vis[N];
    int st[30][N<<1];int id[N];int dfn[N<<1];int ans[N];
    void dfs2(int u,int ff){
        id[u]=++I;dfn[I]=u;st[0][I]=u;dep[u]=dep[ff]+1;
        for(register int v,i=head[u];i;i=a[i].next){
            v=a[i].to;
            if(v==ff) continue;
            dis[v]=dis[u]+a[i].w;
            dfs2(v,u);dfn[++I]=u;st[0][I]=u;
        }
    }
    inline int check(int a,int b){return (dep[a]void Pre(){
        for(register int i=1;i<=I;++i) if((1<<log[i-1])log[i]=log[i-1]+1;else log[i]=log[i-1];
        for(register int k=1;k<=log[I];++k)
            for(register int i=1;i+(1<1<=I;++i)
                st[k][i]=check(st[k-1][i],st[k-1][i+(1<1)]);
        return;
    }
    inline int LCA(int a,int b){
        if(a==b) return a;
        register int l=id[a],r=id[b];
        if(l>r) swap(l,r);
        register int D=log[r-l+1]-1;
        return check(st[D][l],st[D][r-(1<1]);
    }
    void find_root(int u,int ff){
        size[u]=1;f[u]=0;
        for(register int v,i=head[u];i;i=a[i].next){
            v=a[i].to;
            if(vis[v]||v==ff) continue;
            find_root(v,u);
            f[u]=max(f[u],size[v]);
            size[u]+=size[v];
        }
        f[u]=max(f[u],SZ-size[u]);
        if((rt==-1)||f[u]return;
    }
    void build(int u,int ff){
        fa[u]=ff;
        vis[u]=1;int sz=SZ;
        for(register int v,i=head[u];i;i=a[i].next){
            v=a[i].to;
            if(vis[v]) continue;
            rt=-1;SZ=(size[v]>size[u]? sz-size[u]:size[v]);
            find_root(v,0);
            build(rt,u);
        }
        return;
    }
    inline int Dis(int a,int b){
        register int res=dis[a]+dis[b]-2*dis[LCA(a,b)];
        return res;
    }
    inline void insert(int i){
        register int r1=B[i].Get();B[i].pop();register int r2=B[i].Get();B[i].push(r1);if(r1!=-INF&&r2!=-INF) ANS.push(r1+r2);
    }
    inline void del(int i){
        register int r1=B[i].Get();B[i].pop();register int r2=B[i].Get();B[i].push(r1);if(r1!=-INF&&r2!=-INF) ANS.Del(r1+r2);
    }
    void solve(){
        dfs2(1,0);Pre();rt=-1;SZ=n;find_root(1,0);build(rt,0);
        for(register int i=1;i<=n;++i){
            register int p=i;B[i].push(0);
            while(fa[p]!=0) {A[p].push(Dis(i,fa[p]));p=fa[p];}
        }
        for(register int i=1;i<=n;++i){
            if(fa[i]!=0) {
                register int g=A[i].Get();
                if(g!=-INF) B[fa[i]].push(g);
            }
        }
        for(register int i=1;i<=n;++i) insert(i);
        char ch;
        for(register int i=1;i<=m;++i){
            ch=getchar();
            while(ch!='G'&&ch!='C') ch=getchar();
            if(ch=='G'){
                if(!num) {puts("-1");continue;}
                if(num==1) {puts("0");continue;}
                register int anss=ANS.Get();
                if(anss>0)printf("%d\n",anss);
                else puts("0");
            }
            else{
                int g=read();
                num+=(city[g]? 1:(-1));city[g]=(!city[g]);
                register int data,p;
                if(!city[g]){
                    del(g);B[g].push(0);insert(g);
                    for(p=g;fa[p];p=fa[p]){
                        del(fa[p]);data=A[p].Get();
                        if(data!=-INF) B[fa[p]].Del(data);//不删的话就一颗子树里有两个点啦(就WA啦)
                        A[p].push(Dis(g,fa[p]));
                        data=A[p].Get();if(data!=-INF) B[fa[p]].push(data);//重新加入一个点
                        insert(fa[p]);//重新加入答案
                    }
                }
                else{
                    del(g);B[g].Del(0);insert(g);
                    for(p=g;fa[p];p=fa[p]){
                        del(fa[p]);data=A[p].Get();
                        if(data!=-INF) B[fa[p]].Del(data);//删错了也会再加入(最大的永远是最大的)
                        A[p].Del(Dis(g,fa[p]));
                        data=A[p].Get();
                        if(data!=-INF) B[fa[p]].push(data);
                        insert(fa[p]);
                    }
                }
            }
        }
    }
}
int main()
{
    Tree::Build();
    Tree::solve();
}

你可能感兴趣的:(======题解======,点分治,——分治——)