您的位置:首页 > 其它

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