bzoj1095 (点分树)

本文介绍了一种使用点分树解决动态更新树中节点颜色并查询最远黑点对距离的问题。通过维护多个堆,如子树中最远黑点距离、每个黑点到父节点距离及节点答案,实现高效查询和更新。采用multiset替代堆,简化代码但牺牲一定效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:n个节点的树,m次操作,每次将白点变黑,将黑点变白,或询问最远黑点对的距离。

若无修改,可直接树形dp或点分求即可,加上修改的话就要用到点分树了(orz括号序列的做法)。。。考虑没修改时点分治的做法为对每个root,最远点对即为不同子树上最远和次远黑点的距离之和,所以对于点分树的每个结点 X 用个堆S维护X的每棵子树最远黑点距离,以及一个堆T维护 X 所在子树的每个黑点与上一层节点 fa[x] 的距离。然后在加个堆ans维护每个节点的答案即可。。

这里用的multiset来代替的堆,写法较容易,但时间也慢得飞起。。

 

#include<iostream>
#include<stdio.h>
#include<set>
#include<vector>
#include<math.h>
using namespace std;
vector<int>g[100010];
int son[100010],siz[100010],ma[100010],vis[100010],root,A[100010];
int dis[100010][20],dep[100010],fa[100010][20];
void dfsroot(int u,int f,int sum){
     int i,v;
     son[u]=0;
     siz[u]=1;
     for(i=0;i<g[u].size();i++){
        v=g[u][i];
        if(v!=f&&vis[v]==0){
            dfsroot(v,u,sum);
            siz[u]+=siz[v];
            if(siz[son[u]]<siz[v]) son[u]=v;
        }
     }
     ma[u]=max(siz[son[u]],sum-siz[u]);
     if(root==0||ma[u]<ma[root]) root=u;
}
void dfsdis(int u,int f,int k,int d){     //预处理出每个节点到每一层根节点的距离,下面就不需要用lca来求距离辣
     int i,v;
     dep[u]++;
     fa[u][dep[u]]=k;
     dis[u][dep[u]]=d;
     for(i=0;i<g[u].size();i++){
          v=g[u][i];
          if(v!=f&&vis[v]==0) dfsdis(v,u,k,d+1);
     }
}
void dfs(int u){
     int i,v;
     vis[u]=1;
     dfsdis(u,0,u,0);
     for(i=0;i<g[u].size();i++){
        v=g[u][i];
        if(vis[v]==0){
              root=0;
              dfsroot(v,0,siz[v]);
              dfs(root);
        }
     }
}
multiset<int>s[100010],t[100010],ans;
multiset<int>::iterator it;
int max1(int u){
    if(t[u].size()==0) return -1;
    it=t[u].end();
    it--;
    return *it;
}
int max2(int u){
    if(s[u].size()<2) return -1;
    it=s[u].end();
    it--;
    int a=*it;
    it--;
    return a+*it;
}
void update(int u){
    int i,f,ff;
    for(i=dep[u];i;i--){
        f=fa[u][i];
        ff=fa[u][i-1];
        if(i==dep[u]){
            if(max2(f)!=-1) ans.erase(ans.find(max2(f)));
            if(A[u]) s[f].erase(0);
            else s[f].insert(0);
            if(max2(f)!=-1) ans.insert(max2(f));
        }
        if(ff){
            if(max2(ff)!=-1) ans.erase(ans.find(max2(ff)));
            if(max1(f)!=-1) s[ff].erase(s[ff].find(max1(f)));
            if(A[u]) t[f].erase(t[f].find(dis[u][i-1]));
            else t[f].insert(dis[u][i-1]);
            if(max1(f)!=-1) s[ff].insert(max1(f));
            if(max2(ff)!=-1) ans.insert(max2(ff));
        }
    }
}
int main(){
    int i,n,a,b,m;
    char c[10];
    scanf("%d",&n);
    for(i=1;i<n;i++){
        scanf("%d%d",&a,&b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    root=0;
    dfsroot(1,0,n);
    dfs(root);
    for(i=1;i<=n;i++){
        update(i);
        A[i]=1;
    }
    scanf("%d",&m);
    while(m--){
        scanf("%s",c);
        if(c[0]=='C'){
            scanf("%d",&a);
            update(a);
            A[a]=!A[a];
        }
        else{
            if(ans.size()==0) printf("-1\n");
            else{
                it=ans.end();
                it--;
                printf("%d\n",*it);
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值