bzoj1095:Hide 捉迷藏(动态树分治)
来源:互联网 发布:mac充电在哪 编辑:程序博客网 时间:2024/06/05 18:51
我月考时一直在想动态树分治是个什么东西,直到看了这题题解才有点懂。
题面
题意:给出一棵黑白树,每次翻转一个点的颜色,或询问两个黑点间的最远距离。
由于是点对问题,可以想点分治。先考虑问题的静态版本,怎么用点分治求最远两个黑点的距离。
显然,对于一个重心,我们要知道每个连通块中,距离它最远的点的距离,前二大的加起来可以更新答案。
根据点分治的划分过程,每个重心向它的次级重心连边,形成了一棵点分树。显然点分树的深度不超过logN。这就意味着,给每个点开个数据结构,存储其点分树子树的所有点,每个点的信息只出现了logN次,总空间不超过NlogN。
然后考虑这个问题的动态版本,由于每个点的信息只出现了logN次,我们就暴力更新这logN个信息。根据本题要维护的信息,我们可以用三个大根堆实现。
每个重心维护一个堆C,存储其块内所有点到它父重心的路径长。
每个重心维护一个堆B,表示它每个连通块中到它的最长路径,即是每个子树堆C的堆顶。
在用一个全局堆A统计答案,即所有堆B中的前二大的和。
有一种改进STL优先队列使其可以删除某个元素的方法:
多开一个优先队列存储已经删除的元素,求堆顶时若两个堆堆顶相同就一起把堆顶弹出。
据说还有一种线段树维护括号序列的方法。
#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>#include <queue>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int N=200200,oo=1e9;void read(int &hy){ hy=0; char cc=getchar(); while(cc<'0'||cc>'9') cc=getchar(); while(cc>='0'&&cc<='9') { hy=(hy<<3)+(hy<<1)+cc-48; cc=getchar(); }}struct yy{ priority_queue<int> R,D; void push(int x) { R.push(x);} void pop(int x) { D.push(x);} int top() { while(!D.empty() && R.top()==D.top()) R.pop(),D.pop(); return R.top(); } int top2() { int tmp=top(),ans; pop(tmp); ans=tmp+top(); push(tmp); if(ans==tmp||tmp==0) return 0; return ans; }}A,B[N],C[N];int to[N],nex[N],head[N],cnt;int n,m,num,col[N];bool vis[N];int dep[N],f[N],pre[N][18],p[N],siz[N],g[N],tim[N],times,sum,root;void add(int u,int v){ to[++cnt]=v; nex[cnt]=head[u]; head[u]=cnt;}void dfs(int x,int fa){ if(!dep[x]) dep[x]=dep[fa]+1; f[x]=fa; if(!pre[x][0]) pre[x][0]=fa; if(!tim[x]) tim[x]=++times; g[x]=0; siz[x]=1; for(int h=head[x];h;h=nex[h]) if(to[h]!=fa&&!vis[to[h]]) { dfs(to[h],x); siz[x]+=siz[to[h]]; g[x]=max(g[x],siz[to[h]]); } g[x]=max(g[x],sum-siz[x]); if(g[x]<g[root]) root=x;}int dis(int x,int y){ if(x==y) return 0; if(tim[x]>tim[y]) swap(x,y); int hy=y; for(int i=17;i>=0;i--) if(tim[pre[y][i]]>tim[x]) y=pre[y][i]; return dep[x]+dep[hy]-2*dep[pre[y][0]];}void dfs2(int x){ siz[f[x]]=sum-siz[x]; vis[x]=1; for(int h=head[x];h;h=nex[h]) if(!vis[to[h]]) { sum=siz[to[h]]; root=0; dfs(to[h],to[h]); p[root]=x; dfs2(root); }}void update(int x){ A.pop(B[x].top2()); if(!col[x]) B[x].push(1); else B[x].pop(1); A.push(B[x].top2()); for(int now=x;p[now];now=p[now]) { int len=dis(x,p[now])+1; A.pop(B[p[now]].top2()); B[p[now]].pop(C[now].top()); if(!col[x]) C[now].push(len); else C[now].pop(len); B[p[now]].push(C[now].top()); A.push(B[p[now]].top2()); } num-=col[x]; col[x]=!col[x]; num+=col[x];}int main(){ cin>>n; for(int i=1;i<n;i++) { int u,v; read(u); read(v); add(u,v); add(v,u); } g[0]=oo; sum=n; dfs(1,1); dfs2(root); for(int j=1;j<=17;j++) for(int i=1;i<=n;i++) pre[i][j]=pre[pre[i][j-1]][j-1]; for(int i=1;i<=n;i++) { B[i].push(0),B[i].push(0); C[i].push(0),C[i].push(0); A.push(0); } for(int i=1;i<=n;i++) update(i); cin>>m; while(m--) { char cc=getchar(); while(cc!='G'&&cc!='C') cc=getchar(); if(cc=='G') { if(num<=1) printf("%d\n",num-1); else printf("%d\n",A.top()-2); } else { int x; read(x); update(x); } } return 0;}
阅读全文
1 0
- bzoj1095:Hide 捉迷藏(动态树分治)
- 【BZOJ1095】[ZJOI2007]Hide 捉迷藏【动态树分治】
- [BZOJ1095][ZJOI2007]Hide捉迷藏-动态树分治
- 括号序列 || 动态树分治 bzoj1095【ZJOI2007】Hide 捉迷藏
- [bzoj1095][ZJOI2007]Hide 捉迷藏(动态点分治)
- BZOJ1095 Hide 捉迷藏 分治
- [动态点分治] BZOJ1095: [ZJOI2007]Hide 捉迷藏
- bzoj1095 Hide 捉迷藏 动态点分治+堆 (附动态点分治详解)
- bzoj1095: [ZJOI2007]Hide 捉迷藏(动态点分治+树上ST表)
- 【BZOJ1095】捉迷藏,动态点分治
- BZOJ1095 [ZJOI2007]捉迷藏 动态点分治
- [BZOJ1095][ZJOI2007][线段树]Hide捉迷藏
- bzoj1095 Hide 捉迷藏 括号序列&线段树
- [BZOJ1095]Hide 捉迷藏--括号序列&&线段树
- bzoj1095: [ZJOI2007]Hide 捉迷藏
- BZOJ1095: [ZJOI2007]Hide 捉迷藏
- BZOJ1095: [ZJOI2007]Hide 捉迷藏
- BZOJ1095 [ZJOI2007]Hide 捉迷藏
- hive0.13的搭建配置
- 错排&放苹果
- html转pdf文件
- Selective Search for object recognition(含代码)
- 概率性机器学习与人工智能
- bzoj1095:Hide 捉迷藏(动态树分治)
- android databinding绑定教程
- 创建一个类并创建属于该类的对象
- SpringMVC 国际化信息校验
- 程序员笑话(持续更新)
- POJ 2601|URAL 1047|Simple Caluculations|数学推导
- Django 一张网站框架图
- 小故事有大能量(爱情故事)
- 常用的Python os模块