其他分享
首页 > 其他分享> > P2056 [ZJOI2007]捉迷藏

P2056 [ZJOI2007]捉迷藏

作者:互联网

【题意】

给一个树,初始点权全部为0,要求你支持如下操作

1.把一个点的点权异或上1   2.查询树上点权为0的两点之间距离最大的距离

【分析】

仍然考虑先建立点分树,然后对于每个点记录如下信息,开两个可删除的优先级队列记录子树内对自己的贡献,和子树内对fa的贡献

动态维护这些信息,查询的时候简单讨论一下,构成一个链即可

【代码】

有亿点点卡常,可能是我在初始化的实现方式上的问题,其实可以在建立点分树的时候直接做一遍点分治

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int head[maxn],tot,q,n;
struct edge
{
    int to,nxt;
}e[maxn<<1];
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
void add(int x,int y)
{
    e[++tot].to=y; e[tot].nxt=head[x]; head[x]=tot;
}
//点分树部分开始___________________________________________________________
int vis[maxn],size,gsiz,siz[maxn],root;
int fa[maxn];
void findrt(int u,int fa)
{
    siz[u]=1;
    int maxsiz=0;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to] || to==fa) continue;
        findrt(to,u);
        siz[u]+=siz[to];
        maxsiz=max(maxsiz,siz[to]);
    }
    maxsiz=max(maxsiz,size-siz[u]);
    if(maxsiz<gsiz)
    {
        gsiz=maxsiz;
        root=u;
    }
}
void solve(int u)
{
    vis[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        size=gsiz=siz[to];
        findrt(to,0);
        fa[root]=u;
        solve(root);
    }
}
//点分树部分开始___________________________________________________________

//可删除优先级队列部分开始_________________________________________________
struct PQ
{
    priority_queue <int> p,q;
    int top()
    {
        while(!q.empty() && p.top()==q.top()) p.pop(),q.pop();
        if(p.empty()) return 0;
        return p.top();
    }
    void pop()
    {
        while(!q.empty() && p.top()==q.top()) p.pop(),q.pop();
        if(!p.empty()) p.pop();
    }
    int size()
    {
        return p.size()-q.size();
    }
    int ctop()
    {
        if(size()<2) return 0;
        int t=top(); pop();
        int ans=top();
        p.push(t); return ans;
    }
};
//可删除优先级队列部分结束_________________________________________________

//ST表O(1)求LCA部分开始____________________________________________________
int st[maxn<<1][20],dfn[maxn],cs,dfstime,lg[maxn<<1];
int dep[maxn];
void dfs(int u,int fa)
{
    dfn[u]=++dfstime;
    st[dfn[u]][0]=dep[u];
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa) continue;
        dep[to]=dep[u]+1;
        dfs(to,u);
        st[++dfstime][0]=dep[u];
    }    
}
void lca_init()
{
    lg[0]=-1;
    for(int i=1;i<=n*2;i++) lg[i]=lg[i>>1]+1;
    while((1<<(cs+1))<=n*2) cs++;
    for(int j=1;j<=cs;++j)
        for(int i=1;i+(1<<j)-1<=(n<<1);++i)
            st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
int lcadis(int x,int y)
{
    if(dfn[x]>dfn[y]) swap(x,y);
    int i=lg[dfn[y]-dfn[x]+1];
    return min(st[dfn[x]][i],st[dfn[y]-(1<<i)+1][i]);
}
int calcdis(int x,int y)
{
    return dep[x]+dep[y]-2*lcadis(x,y);
}
//ST表O(1)求LCA部分结束____________________________________________________ 

//修改查询部分开始_________________________________________________________ 
PQ A,B[maxn],C[maxn];
//A表示答案,就是拼成的一条链
//B表示所有子节点到自己距离
//C表示所有子节点 
int light[maxn],cntoff;
void turnoff(int u,int v)
{
    if(u==v)
    {
        B[u].p.push(0);
        if(B[u].size()==2) A.p.push(B[u].top());
    }
    if(!fa[u]) return;
    int ff=fa[u],d=calcdis(ff,v),tp=C[u].top();
    C[u].p.push(d);
    if(tp<d)
    {
        int mx=B[ff].top()+B[ff].ctop(),sz=B[ff].size();
        if(tp) B[ff].q.push(tp);
        B[ff].p.push(d);
        int now=B[ff].top()+B[ff].ctop();
        if(now>mx)
        {
            if(sz>=2) A.q.push(mx);
            if(B[ff].size()>=2) A.p.push(now);
        }
    }    
    turnoff(ff,v);
}
void turnon(int u,int v)
{
    if(u==v)
    {
        if(B[u].size()==2) A.q.push(B[u].top()); 
        B[u].q.push(0);
    }
    if(!fa[u]) return;
    int ff=fa[u],d=calcdis(ff,v),tp=C[u].top();
    C[u].q.push(d);
    if(d==tp)
    {
        int mx=B[ff].top()+B[ff].ctop(),sz=B[ff].size();
        B[ff].q.push(d);
        if(C[u].top()) B[ff].p.push(C[u].top());
        int now=B[ff].top()+B[ff].ctop();
        if(now<mx)
        {
            if(sz>=2) A.q.push(mx);
            if(B[ff].size()>=2) A.p.push(now);
        }
    }
    turnon(ff,v);
}
//修改查询部分开始_________________________________________________________
int main()
{

    scanf("%d",&n);
    int x,y;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    dfs(1,0); lca_init();
    size=gsiz=n; findrt(1,0);
    solve(root);
    for(int i=1;i<=n;i++) C[i].p.push(0);
    for(int i=1;i<=n;i++) light[i]=1;
    for(int i=1;i<=n;i++) turnoff(i,i);
    cntoff=n;
    char op[3];
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%s",op);
        if(op[0]=='G')
        {
            if(cntoff<=1) printf("%d\n",cntoff-1);
            else printf("%d\n",A.top()); 
        }
        else
        {
            x=read();
            if(!light[x]) turnoff(x,x),cntoff++;
            else turnon(x,x),cntoff--;
            light[x]^=1;
        }
    }
    return 0;
}

 

标签:ch,P2056,捉迷藏,top,int,ff,push,ZJOI2007,size
来源: https://www.cnblogs.com/andylnx/p/14791927.html