显然只有一次询问的话,可以用点分治来实现。

但是现在我们有多组询问,还带有修改,我们只能通过动态点分治来做了。

动态点分治的主要思想:省去每次点分治求重心的过程,直接预处理出来(因为树的形态不会改变),建立点分树。那么我们每次分治时只需按照点分树上的路径走就是了。

例如,对于这么一颗树:(样例,1为根)(感谢绘图网站https://csacademy.com/app/graph_editor/)

建出来的点分树是这样的:(3为根)


注意:点分树只是把在原树中点分治遍历的顺序建了出来,如果求 disdisdis 或 lcalcalca 还是要在原树中求,不能在点分树上求,因为点分树改变了原树形态。所以如果题目的询问不是针对全局的,而是带有父子关系的(比如多次询问以 uuu 为根的子树中路径长度为 kkk 的路径个数)就不能建点分树了。

应该不能吧,不知道有没有一种玄学的方法

然后我们对于每个点,维护四个大根堆:

  1. dis1dis1dis1,维护:在点分树中以 uuu 为根的子树中,所有灭灯的节点到 uuu 的 fafafa 的距离。
  2. erase1erase1erase1,因为我们有时要从 dis1dis1dis1 中删去一些值,所以 erase1erase1erase1 维护在 dis1dis1dis1 中要删去的值。
  3. dis2dis2dis2,维护:在点分树中 uuu 的所有儿子的 dis1dis1dis1 的堆顶。那么将 dis2dis2dis2 的 top1top1top1 和 top2top2top2 取出来,再相加,就是合法的经过 uuu 的最长路径的长度。
  4. erase2erase2erase2,和 erase1erase1erase1 差不多,用来维护在 dis2dis2dis2 中要删去的值。

由于对于 dis1dis1dis1 和 dis2dis2dis2 都有一个删除堆,所以我把 dis1dis1dis1、erase1erase1erase1 封装在一起,称为 heap1heap1heap1;dis2dis2dis2、erase2erase2erase2封装在一起,称为 heap2heap2heap2。那么这两个 heapheapheap 都可以实现 top1()top1()top1()、top2()top2()top2()、pop()pop()pop()、erase()erase()erase() 和 size()size()size() 操作。

然后我们维护一个全局 heapheapheap:AnsAnsAns 堆,同样有一个 eraseeraseerase 堆。AnsAnsAns用来维护全局 dis2dis2dis2 堆的 top1top1top1 和 top2top2top2 之和。

那么如果询问,答案就是 AnsAnsAns 堆堆顶。

考虑如果修改一个点,那么只会对它的所有祖先的 dis1dis1dis1、dis2dis2dis2 有影响。

那么我们记录下询问点 Qpoint=uQpoint=uQpoint=u,然后让 uuu 往上跳,更新 dis1dis1dis1 和 dis2dis2dis2。

代码和注释如下:

#include<bits/stdc++.h>#define N 100010
#define INF 0x7fffffffusing namespace std;struct heap
{priority_queue<int>q1,q2;int size(){return q1.size()-q2.size();}void push(int x){q1.push(x);} void erase(int x){q2.push(x);}void pop(){while(!q2.empty()&&q1.top()==q2.top())q1.pop(),q2.pop();q1.pop();}int top(){while(!q2.empty()&&q1.top()==q2.top())q1.pop(),q2.pop();return q1.empty()?0:q1.top();}int top2(){if(size()<2)return 0;int x=top();pop();int y=top();push(x);return y;}
}q,q1[N],q2[N];
//q全局路径最大
//q1距离自己父亲距离最大
//q2距离自己距离最大(每个儿子仅有一条路径) int n,Q,nn,root,sum;
int cnt,head[N],nxt[N<<1],to[N<<1];
int size[N],maxsize[N];
int d[N],f[N][17];
int fa[N],ans[N];
bool vis[N],open[N];void adde(int u,int v)
{to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}//------------------------------------------------------------倍增求点之间的距离
void dfs(int u)
{for(int i=1;i<=16;i++)f[u][i]=f[f[u][i-1]][i-1];for(int i=head[u];i;i=nxt[i]){int v=to[i];if(v==f[u][0])continue;f[v][0]=u;d[v]=d[u]+1;dfs(v);}
}int lca(int a,int b)
{if(d[a]<d[b])swap(a,b);for(int i=16;i>=0;i--)if(d[f[a][i]]>=d[b])a=f[a][i];if(a==b)return a;for(int i=16;i>=0;i--)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];return f[a][0];
}int getdis(int a,int b)
{return d[a]+d[b]-2*d[lca(a,b)];
}//------------------------------------------------------------建点分树
void getroot(int u,int fa)
{size[u]=1,maxsize[u]=0;for(int i=head[u];i;i=nxt[i]){int v=to[i];if(v==fa||vis[v])continue;getroot(v,u);size[u]+=size[v];maxsize[u]=max(maxsize[u],size[v]);}maxsize[u]=max(maxsize[u],nn-size[u]);if(maxsize[u]<maxsize[root])root=u;
}void maketree(int u)
{vis[u]=true;for(int i=head[u];i;i=nxt[i]){int v=to[i];if(vis[v])continue;nn=size[v],root=0;getroot(v,u);fa[root]=u;//记录点分树中的famaketree(root);}
}//------------------------------------------------------------修改
void update(int u)
{if(!open[u]){sum++;q2[u].push(0);//push(0)是为了保证当灯是关着时,q2[u]的size至少为1if(q2[u].size()==2) q.push(q2[u].top());}else{sum--;if(q2[u].size()==2) q.erase(q2[u].top());q2[u].erase(0);}int now=u;while(1){int dis=getdis(fa[now],u),t1;if(!fa[now])return;if(!open[u])t1=q1[now].top(),q1[now].push(dis);//更新q1else q1[now].erase(dis),t1=q1[now].top();if(dis>t1){int s1=q2[fa[now]].top()+q2[fa[now]].top2();int siz=q2[fa[now]].size();if(!open[u])//更新q2{if(t1) q2[fa[now]].erase(t1);q2[fa[now]].push(dis);}else{q2[fa[now]].erase(dis);if(t1) q2[fa[now]].push(t1);}int s2=q2[fa[now]].top()+q2[fa[now]].top2();if(s2!=s1)//更新Ans堆{if(siz>=2) q.erase(s1);if(q2[fa[now]].size()>=2)q.push(s2);}}now=fa[now];}
}//------------------------------------------------------------主程序
int main()
{scanf("%d",&n);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);adde(u,v),adde(v,u);}d[1]=1;dfs(1);nn=n,maxsize[0]=INF;getroot(1,0);maketree(root);for(int i=1;i<=n;i++)open[i]=true;for(int i=1;i<=n;i++)update(i),open[i]=false;scanf("%d",&Q);while(Q--){char ch=getchar();while(ch!='C'&&ch!='G')ch=getchar();if(ch=='G'){if(sum>=2) printf("%d\n",q.top());else if(sum==1) puts("0");else puts("-1");}if(ch=='C'){int u;scanf("%d",&u);open[u]^=1;update(u);}}return 0;
}

【ZJOI2007】捉迷藏(动态树分治)相关推荐

  1. BZOJ1095 [ZJOI2007]捉迷藏 动态点分治

    每次修改一个点的黑白状态,询问树上最远黑点距离 拿这个题做动态点分治模板题:(%%%PoPoQQQ大爷) 点分治的过程是对树块找重心之后分成多个小树块,降低规模分别处理的过程,把链的信息收到其中&qu ...

  2. P4719 【模板】“动态 DP“动态树分治(矩阵/轻重链剖分/ddp)

    P4719 [模板]"动态 DP"&动态树分治 求解树上最大权独立集,但是需要支持修改. https://www.luogu.com.cn/problem/solution ...

  3. [BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA]

    [BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA] 题目大意: 给定一颗nn个节点的树,边权均为11,初始每个点权值为00 . 其中操作QQ xx询问x点的点权,操作 MM xx dd ...

  4. bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习

    好迷啊...感觉动态点分治就是个玄学,蜜汁把树的深度缩到logn (静态)点分治大概是递归的时候分类讨论: 1.答案经过当前点,暴力(雾)算 2.答案不经过当前点,继续递归 由于原树可以长的奇形怪状( ...

  5. 【BZOJ4372】烁烁的游戏 动态树分治+线段树

    [BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠. 题意: 给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠. 烁烁他每次会跳到一个节点u,把周围 ...

  6. BZOJ 3730: 震波 动态树分治 线段树 lca

    3730: 震波 Time Limit: 15 Sec  Memory Limit: 256 MB Submit: 1202  Solved: 288 [Submit][Status][Discuss ...

  7. BZOJ1095: [ZJOI2007]Hide 捉迷藏(动态点分治)

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条 ...

  8. BZOJ 4012: [HNOI2015]开店 -- 动态树分治

    4012: [HNOI2015]开店 Time Limit: 70 Sec  Memory Limit: 512 MB Submit: 1463  Solved: 635 [Submit][Statu ...

  9. 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治

    [BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...

最新文章

  1. C# JSON格式的字符串读取到类中
  2. ArcGIS 10 五大飞跃
  3. request获得请求参数
  4. 二分查找和折半插入排序一块说说-很合适~~~
  5. 【渝粤教育】广东开放大学 计算机思维 形成性考核 (29)
  6. 繁体简体转换器 v 1.0
  7. 能在市场上大概率赚钱的人类型
  8. Secret 的使用场景 - 每天5分钟玩转 Docker 容器技术(109)
  9. java 跟 咖啡的关系
  10. CAN波特率计算公式
  11. 从“断臂求生”到一骑绝尘,航运巨头马士基如何利用区块链技术力挽狂澜?
  12. vs2015遇见问题:后面有“::”的名称一定是类名或命名空间名
  13. Java消息队列三道面试题详解
  14. 参加 TechEd 2004
  15. Linux下for语句
  16. 计算机毕业设计java+ssm鲜花销售商城信息网站(源码+系统+mysql数据库+Lw文档)
  17. Exception in thread “main“ java.lang.Error: Unresolved compilation problem: at second.math_practi
  18. 基于SSM实现的求职招聘系统【附源码】(毕设)
  19. pta 软硬车厢交替排列
  20. 使用signalR创建聊天室。

热门文章

  1. java 16进制字符转10进制_java 16进制字符串怎么转换成10进制字符串
  2. 《目标检测蓝皮书》第1篇 机器学习基础
  3. 计算机专业英语名人名言,英语名人名言(中英对照)
  4. 隔板法详解(各种方法)
  5. 【数论】 排列组合中的隔板问题
  6. C语言特殊图案之菱形的三种方法---今日笔记
  7. 聊一下三极管截止、放大和饱和3种工作状态
  8. Excel坐标点导入ArcGIS坐标不匹配问题
  9. 广告深度学习计算:阿里妈妈智能创意服务优化
  10. 解决Ubuntu端口占用问题