bzoj 1095: [ZJOI2007]Hide 捉迷藏 动态树分治+堆

题意

有一棵树,每个节点上有一盏灯,一开始全是关的。要求资瓷两个操作
C x表示将x的灯的状态改变
G表示查询最远的两个关着的灯泡的距离。
n<=100000,m<=200000

分析

传说中的动态树分治啊~~

我们先按照树分治的顺序建一棵新的树,也就是每个节点的父亲为他的上一个分治中心。
显然这棵树的深度不会超过logn。
然后我们在这棵树上每个节点维护两个堆:
b:表示该节点的子树到达其父亲的路径
c:表示该节点所有儿子的b的堆顶
再维护一个a储存所有b的最大值+次大值。
修改的话沿着该节点往上跑,然后更新路径上的堆就好啦。

查询距离的话用rmq是很棒棒的。

代码

#include
#include
#include
#include
#include
#include
#include
using namespace std;

const int N=100005;

int n,m,fa[N],dep[N],pos[N],rmq[N*2][20],cnt,dfn,last[N],lg[N*2],size[N],mx[N],tot,root;
bool vis[N],clo[N];
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[N],c[N];

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]);
    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]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 (nowif (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;iint 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<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());
    }
}

你可能感兴趣的:(动态树分治,堆)