[ZJOI 2007] 捉迷藏

题目描述:

给出一棵树,每个点有颜色,初始颜色为黑。
C:U 令U 的颜色取反
G:询问树中最远的黑色点距离是多少?

题目分析:

巨巨们都太神了,居然把这题目,当做动态点分的模板(雾
如果没有修改操作,考虑如何用裸的点分统计答案
对于每个点我们只需要知道他作为重心时经过他的两个黑点形成的最长链.
可以通过子节点获取信息
对于需要进行动态修改以及动态获取最大值,堆应该是一个不错的选择。

对于每个点维护两个堆,第一个堆C维护重心的父重心的子树中的点到父重心的距离
第二个堆B维护这个重心的所有子重心中黑点的最大深度(也就是所有子重心的C堆堆顶)。最后用一个堆A维护所有重心B堆的最大值和次大值的和,A的堆顶就是答案,注意如果某个点是黑点,那么这个点的B堆中还要加入一个距离0,确保这个点和子树中的黑点可以形成合法的路径并更新答案。

修改的话暴力修改父重心就好了,点分树的优良性质就是logn的深度

另外 STL的堆没有删除的操作,那么就维护两个堆就好了。

本题还有边分和括号序列做法orzzzzzzzzzzzzzz
复杂度都比动态点分优!

题目链接:

Luogu 2056
BZOJ 1095

Ac 代码:

#include 
#include 
#include 
#include 
#include 
const int maxm=200003;
struct heap{
    std::priority_queue<int> a,b;
    inline void push(int x){a.push(x);}
    inline void del(int x){b.push(x);}
    inline void update()
    {
        while(!b.empty()&&a.top()==b.top()) 
         a.pop(),b.pop();
    }
    inline void pop(){update();a.pop();}
    inline int top(){update();return a.size()?a.top():0;}
    inline int size(){return a.size()-b.size();}
    inline int sctop()
    {
        if(size()<2) return 0;
        int tx=top();pop();
        int tmp=top();push(tx);
        return tmp;
    }
}a,b[maxm],c[maxm];
int head[maxm],to[maxm<<1],net[maxm<<1],fa[maxm][20],belong[maxm],cnt;
int f[maxm],siz[maxm],deep[maxm],sum;
int pd[maxm],vis[maxm],root,n,m;
inline void addedge(int x,int y)
{
    cnt++,to[cnt]=y,net[cnt]=head[x],head[x]=cnt;
}
void dfs(int now,int fax,int dep)
{
    fa[now][0]=fax,deep[now]=dep;
    //printf("%d\n",now);
    for(int i=1;i<=19;i++)
    {
        if(deep[now]<(1<break;
        fa[now][i]=fa[fa[now][i-1]][i-1];
    }
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fax) dfs(to[i],now,dep+1);
}
inline int lca(int u,int v)
{
    if(deep[u]std::swap(u,v);
    int k=deep[u]-deep[v];
    for(int i=19;~i;i--)
     if((k>>i)&1) u=fa[u][i];
    if(u==v) return u;
    for(int i=19;~i;i--)
     if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
inline int dis(int u,int v)
{
    return deep[u]+deep[v]-2*deep[lca(u,v)];
}
void getroot(int now,int fa)
{
    siz[now]=1,f[now]=0;
    for(int i=head[now];i;i=net[i])
    if(to[i]!=fa&&!vis[to[i]])
    {
        getroot(to[i],now);
        siz[now]+=siz[to[i]];
        f[now]=std::max(f[now],siz[to[i]]);
    }
    f[now]=std::max(f[now],sum-siz[now]);
    if(f[now]void pt(int now,int fax)
{
    //printf("-%d-\n",now);
    vis[now]=1,belong[now]=fax;
    for(int i=head[now];i;i=net[i])
    if(!vis[to[i]])
    {
        sum=siz[to[i]];
        root=0;
        getroot(to[i],now);
        pt(root,now);
    }
}
void turn_off(int u,int v)//白点变黑 
{
    if(u==v)
    {
        b[u].push(0);
        if(b[u].size()==2) a.push(b[u].top());
    }
    if(!belong[u]) return;
    int f=belong[u],distance=dis(f,v),tmp=c[u].top();
    c[u].push(distance);
    if(distance>tmp)
    {
        int mx=b[f].top()+b[f].sctop(),size=b[f].size();
        if(tmp) b[f].del(tmp);
        b[f].push(distance);
        int now=b[f].top()+b[f].sctop();
        if (now>mx)
        {
            if(size>=2) a.del(mx);
            if(b[f].size()>=2) a.push(now);
        }
    }
    turn_off(f,v);
}
void turn_on(int u,int v)//黑点变白
{
    if (u==v)
    {
        if(b[u].size()==2) a.del(b[u].top());
        b[u].del(0);
    }
    if (!belong[u]) return;
    int f=belong[u],distance=dis(f,v),tmp=c[u].top();
    c[u].del(distance);
    if (distance==tmp)
    {
        int mx=b[f].top()+b[f].sctop(),size=b[f].size();
        b[f].del(distance);
        if (c[u].top()) b[f].push(c[u].top());
        int now=b[f].top()+b[f].sctop();
        if(nowif(size>=2) a.del(mx);
            if(b[f].size()>=2) a.push(now);
        }
    }
    turn_on(f,v);
} 
int main()
{
    int n,tot=0;
    scanf("%d",&n);
    for(int i=1;iint u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v),addedge(v,u);
    }
    tot=n;
    dfs(1,0,0); 
    root=0;f[0]=maxm;
    sum=n;
    getroot(1,0);
    pt(root,0);
    for(int i=1;i<=n;i++) c[i].push(0);
    for(int i=1;i<=n;i++) pd[i]=1,turn_off(i,i);
    int q;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        char s[10];
        scanf("%s",s);
        if(s[0]=='C')
        {
            int x;
            scanf("%d",&x);
            if(pd[x]) tot--,turn_on(x,x);
            else tot++,turn_off(x,x);
            pd[x]^=1;
        }
        else
        {
            if(tot<=1) printf("%d\n",tot-1);
            else printf("%d\n",a.top());
        }
    }
    return 0;
}

你可能感兴趣的:(题目分析,点分治,点分树)