bzoj1095:Hide 捉迷藏(动态树分治)

来源:互联网 发布:mac充电在哪 编辑:程序博客网 时间:2024/06/05 18:51

我月考时一直在想动态树分治是个什么东西,直到看了这题题解才有点懂。

题面
题意:给出一棵黑白树,每次翻转一个点的颜色,或询问两个黑点间的最远距离。

由于是点对问题,可以想点分治。先考虑问题的静态版本,怎么用点分治求最远两个黑点的距离。
显然,对于一个重心,我们要知道每个连通块中,距离它最远的点的距离,前二大的加起来可以更新答案。

根据点分治的划分过程,每个重心向它的次级重心连边,形成了一棵点分树。显然点分树的深度不超过logN。这就意味着,给每个点开个数据结构,存储其点分树子树的所有点,每个点的信息只出现了logN次,总空间不超过NlogN。

然后考虑这个问题的动态版本,由于每个点的信息只出现了logN次,我们就暴力更新这logN个信息。根据本题要维护的信息,我们可以用三个大根堆实现。

每个重心维护一个堆C,存储其块内所有点到它父重心的路径长。
每个重心维护一个堆B,表示它每个连通块中到它的最长路径,即是每个子树堆C的堆顶。
在用一个全局堆A统计答案,即所有堆B中的前二大的和。

有一种改进STL优先队列使其可以删除某个元素的方法:
多开一个优先队列存储已经删除的元素,求堆顶时若两个堆堆顶相同就一起把堆顶弹出。

据说还有一种线段树维护括号序列的方法。

#include <iostream>#include <fstream>#include <algorithm>#include <cmath>#include <ctime>#include <cstdio>#include <cstdlib>#include <cstring>#include <queue>using namespace std;#define mmst(a, b) memset(a, b, sizeof(a))#define mmcp(a, b) memcpy(a, b, sizeof(b))typedef long long LL;const int N=200200,oo=1e9;void read(int &hy){    hy=0;    char cc=getchar();    while(cc<'0'||cc>'9')    cc=getchar();    while(cc>='0'&&cc<='9')    {        hy=(hy<<3)+(hy<<1)+cc-48;        cc=getchar();    }}struct yy{    priority_queue<int> R,D;    void push(int x) { R.push(x);}    void pop(int x) { D.push(x);}    int top()    {        while(!D.empty() && R.top()==D.top())        R.pop(),D.pop();        return R.top();    }    int top2()    {        int tmp=top(),ans;        pop(tmp);        ans=tmp+top();        push(tmp);        if(ans==tmp||tmp==0)        return 0;        return ans;    }}A,B[N],C[N];int to[N],nex[N],head[N],cnt;int n,m,num,col[N];bool vis[N];int dep[N],f[N],pre[N][18],p[N],siz[N],g[N],tim[N],times,sum,root;void add(int u,int v){    to[++cnt]=v;    nex[cnt]=head[u];    head[u]=cnt;}void dfs(int x,int fa){    if(!dep[x])    dep[x]=dep[fa]+1;    f[x]=fa;    if(!pre[x][0])    pre[x][0]=fa;    if(!tim[x])    tim[x]=++times;    g[x]=0;    siz[x]=1;    for(int h=head[x];h;h=nex[h])    if(to[h]!=fa&&!vis[to[h]])    {        dfs(to[h],x);        siz[x]+=siz[to[h]];        g[x]=max(g[x],siz[to[h]]);    }    g[x]=max(g[x],sum-siz[x]);    if(g[x]<g[root])    root=x;}int dis(int x,int y){    if(x==y)    return 0;    if(tim[x]>tim[y])    swap(x,y);    int hy=y;    for(int i=17;i>=0;i--)    if(tim[pre[y][i]]>tim[x])    y=pre[y][i];    return dep[x]+dep[hy]-2*dep[pre[y][0]];}void dfs2(int x){    siz[f[x]]=sum-siz[x];    vis[x]=1;    for(int h=head[x];h;h=nex[h])    if(!vis[to[h]])    {        sum=siz[to[h]];        root=0;        dfs(to[h],to[h]);        p[root]=x;        dfs2(root);    }}void update(int x){    A.pop(B[x].top2());    if(!col[x])    B[x].push(1);    else    B[x].pop(1);    A.push(B[x].top2());    for(int now=x;p[now];now=p[now])    {        int len=dis(x,p[now])+1;        A.pop(B[p[now]].top2());        B[p[now]].pop(C[now].top());        if(!col[x])        C[now].push(len);        else        C[now].pop(len);        B[p[now]].push(C[now].top());        A.push(B[p[now]].top2());    }    num-=col[x];    col[x]=!col[x];    num+=col[x];}int main(){    cin>>n;    for(int i=1;i<n;i++)    {        int u,v;        read(u);        read(v);        add(u,v);        add(v,u);    }    g[0]=oo;    sum=n;    dfs(1,1);    dfs2(root);    for(int j=1;j<=17;j++)    for(int i=1;i<=n;i++)    pre[i][j]=pre[pre[i][j-1]][j-1];    for(int i=1;i<=n;i++)    {        B[i].push(0),B[i].push(0);        C[i].push(0),C[i].push(0);        A.push(0);    }    for(int i=1;i<=n;i++)    update(i);    cin>>m;    while(m--)    {        char cc=getchar();        while(cc!='G'&&cc!='C')        cc=getchar();        if(cc=='G')        {            if(num<=1)            printf("%d\n",num-1);            else            printf("%d\n",A.top()-2);        }        else        {            int x;            read(x);            update(x);        }    }    return 0;}

这里写图片描述
这里写图片描述