BZOJ 1095 [ZJOI2007]Hide 捉迷藏
2016-08-09 17:26
330 查看
点分治模板题练习
借(抄)鉴(袭)了这一篇:
思想是通过重心与子重心连边,形成logn高度的树,所以更新节点只需要logn的复杂度。用堆维护。
代码有点长,打了一点注释以后方便理解- -
借(抄)鉴(袭)了这一篇:
http://blog.csdn.net/popoqqq/article/details/44461423
思想是通过重心与子重心连边,形成logn高度的树,所以更新节点只需要logn的复杂度。用堆维护。
代码有点长,打了一点注释以后方便理解- -
#include<queue> #include<cstdio> #include<algorithm> #define MAXN 100005 using namespace std; struct edge{int next,to;bool ban;}e[MAXN<<1]; int T, last[MAXN], ecnt=1, pos[MAXN], a[MAXN<<1][20], log_2[MAXN<<1], dep[MAXN], fa[MAXN]; bool status[MAXN]; struct PQ { //可删堆 priority_queue<int> heap,delmark; void Insert(int x) { heap.push(x); } void Erase(int x) { delmark.push(x); } void Pop() { while(delmark.size() && delmark.top()==heap.top()) heap.pop(),delmark.pop(); heap.pop(); } int Top() { while(delmark.size() && delmark.top()==heap.top()) heap.pop(),delmark.pop(); return heap.top(); } int second_Top() { int temp=Top(); Pop(); int re=Top(); Insert(temp); return re; } int Size() { return heap.size()-delmark.size(); } }s1[MAXN], s2[MAXN], ans; //s1[i]:i为重心的子树中所有点到i的重心父亲的距离 //s2[i]:i所有重心儿子的s1堆的堆顶 //ans: 答案嘛 void add(int a, int b) { e[++ecnt]=(edge){last[a],b,0}; last[a]=ecnt; } int get_size(int x, int from)//得到子树大小 { int re=1; for(int i = last[x]; i; i=e[i].next) { if(e[i].ban||e[i].to==from) continue; re+=get_size(e[i].to,x); } return re; } void dfs(int x, int from, int dep, PQ &s)//树上所有点到某一点的距离入堆 { s.Insert(dep); for(int i = last[x]; i; i=e[i].next) { if(e[i].ban || e[i].to==from) continue; dfs(e[i].to,x,dep+1,s); } } void dfs(int x, int from)//【玄学】 { a[pos[x]=++T][0]=dep[x]=dep[from]+1; for(int i = last[x]; i; i=e[i].next) { if(e[i].to!=from) { dfs(e[i].to,x); a[++T][0]=dep[x]; } } } int LCA_Depth(int x,int y)//【也是玄学】 { x=pos[x];y=pos[y]; if(x>y) swap(x,y); int L=log_2[y-x+1]; return min(a[x][L],a[y-(1<<L)+1][L]); } int Distance(int x,int y)//【都是玄学】 { return dep[x]+dep[y]-2*LCA_Depth(x,y); } int get_center_of_gravity(int x, int from, int size, int &cg)//找树重心 { int re=1; bool flag=true;//flag表示x是否是重心 for(int i = last[x]; i; i=e[i].next) { if(e[i].ban || e[i].to==from) continue; int temp=get_center_of_gravity(e[i].to,x,size,cg); if(temp<<1 > size)flag=false;//重心定义 re+=temp; } if( (size-re)<<1 > size)flag=false;//不要忘记 也是重心定义 if(flag)cg=x; return re; } void Insert(PQ &s) { if(s.Size()>=2) { int temp=s.Top()+s.second_Top(); ans.Insert(temp); } } void Erase(PQ &s) { if(s.Size()>=2) { int temp=s.Top()+s.second_Top(); ans.Erase(temp); } } int tree_divide_and_conquer(int x) { int size=get_size(x,0), cg;//得到当前子树的大小 get_center_of_gravity(x,0,size,cg);//得到当前子树的重心cg s2[cg].Insert(0); for(int i = last[cg]; i; i=e[i].next) { if(!e[i].ban) { e[i].ban=e[i^1].ban=true; PQ s; dfs(e[i].to,0,1,s); int temp=tree_divide_and_conquer(e[i].to); fa[temp]=cg; s1[temp]=s; s2[cg].Insert(s1[temp].Top()); } } Insert(s2[cg]); return cg; } /* 开关总体思想:假设关闭(打开)x节点 ans中删除x重心的贡献 对x的s2堆进行处理(把x自己删除或加入s2[x]) ans中加入x重心的新贡献 不断往父重心找,设父重心y ans中删除y重心的贡献 对y的s2堆进行处理(s2[y]中删除x的贡献->把x自己删除或加入s1[x]->s2[y]中加入x的新贡献) ans中加入y重心的新贡献 也就是不断对x的父重心进行更新ans */ void turn_off(int x) { Erase(s2[x]); s2[x].Erase(0); Insert(s2[x]); for(int i = x; fa[i]; i=fa[i]) { Erase(s2[fa[i]]); if(s1[i].Size()) s2[fa[i]].Erase(s1[i].Top()); s1[i].Erase(Distance(fa[i],x)); if(s1[i].Size()) s2[fa[i]].Insert(s1[i].Top()); Insert(s2[fa[i]]); } } void turn_on(int x) { Erase(s2[x]); s2[x].Insert(0); Insert(s2[x]); for(int i = x; fa[i]; i=fa[i]) { Erase(s2[fa[i]]); if(s1[i].Size()) s2[fa[i]].Erase(s1[i].Top()); s1[i].Insert(Distance(fa[i],x)); if(s1[i].Size()) s2[fa[i]].Insert(s1[i].Top()); Insert(s2[fa[i]]); } } int main() { int n, m, cnt; scanf("%d",&n); cnt=n; for(int x, y, i = 1; i < n; i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } tree_divide_and_conquer(1);//点分治 //【玄学】快速求树上两点距离,我还没看懂。。。 dfs(1,0); for(int i = 2; i <= T; i++) log_2[i]=log_2[i>>1]+1; for(int j = 1; j <= log_2[T]; j++) for(int i = 1; i+(1<<j)-1<=T; i++) a[i][j]=min(a[i][j-1],a[i+(1<<(j-1))][j-1]); //灯状态 for(int i = 1; i <= n; i++) status[i]=true; scanf("%d",&m); for(char s[3]; m--;) { scanf("%s",s); if(s[0]=='G') { if(cnt<=1)printf("%d\n",cnt-1); else printf("%d\n",ans.Top()); } else { int x; scanf("%d",&x); if(status[x]) { cnt--; status[x]=0; turn_off(x); } else { cnt++; status[x]=1; turn_on(x); } } } }
相关文章推荐
- bzoj1095[ZJOI2007]Hide 捉迷藏
- BZOJ 1095【ZJOI2007】Hide捉迷藏
- 【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏
- BZOJ 1095 [ZJOI2007]Hide 捉迷藏 ——动态点分治
- BZOJ1095: [ZJOI2007]Hide 捉迷藏
- [bzoj1095][ZJOI2007]Hide 捉迷藏——线段树+括号序列
- bzoj 1095: [ZJOI2007]Hide 捉迷藏 (动态点分治)
- [BZOJ1095][ZJOI2007][线段树]Hide捉迷藏
- 【BZOJ1095】[ZJOI2007]Hide 捉迷藏 动态树分治+堆
- [BZOJ]1095 Hide捉迷藏(ZJOI2007)
- [bzoj1095][ZJOI2007]Hide 捉迷藏(动态点分治)
- 【BZOJ 1095】 1095: [ZJOI2007]Hide 捉迷藏 (括号序列+线段树)
- bzoj 1095: [ZJOI2007]Hide 捉迷藏
- bzoj1095: [ZJOI2007]Hide 捉迷藏
- BZOJ 1095 ZJOI 2007 Hide 捉迷藏 动态点分治
- bzoj1095 [ZJOI2007]Hide 捉迷藏
- spoj2666 Qtree4(类似于bzoj1095: [ZJOI2007]Hide 捉迷藏)详解
- BZOJ1095: [ZJOI2007]Hide 捉迷藏
- 动态点分治:Bzoj1095: [ZJOI2007]Hide 捉迷藏
- BZOJ_1095_[ZJOI2007]Hide 捉迷藏_动态点分治+堆