您的位置:首页 > 其它

BZOJ 1095 [ZJOI2007]Hide 捉迷藏

2016-08-09 17:26 330 查看
点分治模板题练习

借(抄)鉴(袭)了这一篇:
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);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: