bzoj 1095: [ZJOI2007]Hide 捉迷藏 动态树分治+堆
2017-04-20 16:37
253 查看
题意
有一棵树,每个节点上有一盏灯,一开始全是关的。要求资瓷两个操作C x表示将x的灯的状态改变
G表示查询最远的两个关着的灯泡的距离。
n<=100000,m<=200000
分析
传说中的动态树分治啊~~我们先按照树分治的顺序建一棵新的树,也就是每个节点的父亲为他的上一个分治中心。
显然这棵树的深度不会超过logn。
然后我们在这棵树上每个节点维护两个堆:
b:表示该节点的子树到达其父亲的路径
c:表示该节点所有儿子的b的堆顶
再维护一个a储存所有b的最大值+次大值。
修改的话沿着该节点往上跑,然后更新路径上的堆就好啦。
查询距离的话用rmq是很棒棒的。
代码
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<queue> #include<cmath> using namespace std; const int N=100005; int n,m,fa ,dep ,pos ,rmq[N*2][20],cnt,dfn,last ,lg[N*2],size ,mx ,tot,root; bool vis ,clo ; struct edge{int to,next;}e[N*2]; struct Heap { priority_queue<int> a,b; void push(int x) { a.push(x); } void erase(int x) { b.push(x); } void pop() { while (b.size()&&a.top()==b.top()) a.pop(),b.pop(); a.pop(); } int top() { while (b.size()&&a.top()==b.top()) a.pop(),b.pop(); if (!a.size()) return 0; else return a.top(); } int size() { return a.size()-b.size(); } int stop() { if (size()<2) return 0; int x=top();pop(); int y=top();push(x); return y; } }a,b ,c ; void addedge(int u,int v) { e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt; e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt; } void dfs(int x,int fa) { dep[x]=dep[fa]+1;pos[x]=++dfn;rmq[dfn][0]=dep[x]; for (int i=last[x];i;i=e[i].next) { if (e[i].to==fa) continue; dfs(e[i].to,x); rmq[++dfn][0]=dep[x]; } } int get_dis(int x,int y) { int l=pos[x],r=pos[y]; if (l>r) swap(l,r); int len=lg[r-l+1],mn=min(rmq[l][len],rmq[r-(1<<len)+1][len]); return dep[x]+dep[y]-2*mn; } void get_root(int x,int fa) { size[x]=1;mx[x]=0; for (int i=last[x];i;i=e[i].next) { if (vis[e[i].to]||e[i].to==fa) continue; get_root(e[i].to,x); size[x]+=size[e[i].to]; mx[x]=max(mx[x],size[e[i].to]); } mx[x]=max(mx[x],tot-size[x]); if (!root||mx[x]<mx[root]) root=x; } void build(int x,int y) { vis[x]=1;fa[x]=y; for (int i=last[x];i;i=e[i].next) { if (vis[e[i].to]) continue; root=0;tot=size[e[i].to]; get_root(e[i].to,0); build(root,x); } } void turn_off(int u,int v) { if (u==v) { c[u].push(0); if (c[u].size()==2) a.push(c[u].top()); } if (!fa[u]) return; int f=fa[u],d=get_dis(v,f),tmp=b[u].top(); b[u].push(d); if (d>tmp) { int mx=c[f].top()+c[f].stop(),size=c[f].size(); if (tmp) c[f].erase(tmp); c[f].push(d); int now=c[f].top()+c[f].stop(); if (now>mx) { if (size>=2) a.erase(mx); if (c[f].size()>=2) a.push(now); } } turn_off(f,v); } void turn_on(int u,int v) { if (u==v) { c[u].erase(0); if (c[u].size()==1) a.erase(c[u].top()); } if (!fa[u]) return; int f=fa[u],d=get_dis(v,f),tmp=b[u].top(); b[u].erase(d); if (d==tmp) { int mx=c[f].top()+c[f].stop(),size=c[f].size(); c[f].erase(d); if (b[u].size()) c[f].push(b[u].top()); int now=c[f].top()+c[f].stop(); if (now<mx) { if (size>=2) a.erase(mx); if (c[f].size()>=2) a.push(now); } } turn_on(f,v); } int main() { scanf("%d",&n); for (int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); addedge(x,y); } dfs(1,0); for (int i=1;i<=dfn;i++) lg[i]=log(i)/log(2); for (int j=1;j<=lg[dfn];j++) for (int i=1;i<=dfn-(1<<j)+1;i++) rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]); root=0;tot=n; get_root(1,0); build(root,0); for (int i=1;i<=n;i++) clo[i]=1,turn_off(i,i); scanf("%d",&m); while (m--) { char ch[2]; scanf("%s",ch); if (ch[0]=='C') { int x; scanf("%d",&x); if (clo[x]) clo[x]=0,turn_on(x,x); else clo[x]=1,turn_off(x,x); } else printf("%d\n",a.top()); } }
相关文章推荐
- 【BZOJ1095】[ZJOI2007]Hide 捉迷藏 动态树分治+堆
- BZOJ 1095 ZJOI2007 Hide 捉迷藏 动态树分治+堆
- BZOJ 1095 [ZJOI2007]Hide 捉迷藏
- 【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏 括号序列
- BZOJ1095 [ZJOI2007]Hide 捉迷藏
- BZOJ 1095 [ZJOI2007]Hide 捉迷藏
- BZOJ1095 [ZJOI2007]Hide 捉迷藏 【动态点分治 + 堆】
- [bzoj1095] [ZJOI2007]Hide 捉迷藏
- bzoj1095: [ZJOI2007]Hide 捉迷藏(动态点分治+树上ST表)
- 「BZOJ1095」[ZJOI2007] Hide 捉迷藏
- bzoj千题计划252:bzoj1095: [ZJOI2007]Hide 捉迷藏
- 【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏
- 【BZOJ1095】[ZJOI2007]Hide 捉迷藏【动态树分治】
- 【bzoj 1095】[ZJOI2007]Hide 捉迷藏
- bzoj千题计划245:bzoj1095: [ZJOI2007]Hide 捉迷藏
- bzoj 1095 [ZJOI2007]Hide 捉迷藏(括号序列+线段树)
- bzoj1095[ZJOI2007]Hide 捉迷藏
- BZOJ 1095【ZJOI2007】Hide捉迷藏
- 【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏
- [BZOJ1095]-[ZJOI2007]Hide 捉迷藏-点分树