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

题目链接

题意

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

Sol

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

除了修改和询问都是板子

唯一易错点:

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

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

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<queue>
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;i<n;++i){
            u=read();v=read();w=1;
            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]<dep[b]? a:b);}
    void Pre(){
        for(register int i=1;i<=I;++i) if((1<<log[i-1])<i) 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<<k)-1<=I;++i)
                st[k][i]=check(st[k-1][i],st[k-1][i+(1<<k-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<<D)+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]<f[rt]) rt=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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值