洛谷 P2056 [ZJOI2007]捉迷藏(动态点分治)

题目链接

冒着滥用此题将封号的风险测了七发RP才过……

每个点建两个双堆,堆a表示这个点所在的子树里所有点到他点分树中父亲的距离,堆b表示所有点分树子树中到他距离最大的个点的距离,堆c用来存全局答案,显然全局答案就是每个堆b中最大值和次大值的和

点分树上暴跳父亲复杂度是log的,所以总复杂度是 n l o g 2 n nlog^2n nlog2n

代码如下:

#include
using namespace std;

inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}

struct heap
{
    priority_queue<int> q1,q2;

    void insert(int val)
    {
        q1.push(val);
    }

    void erase(int val)
    {
        q2.push(val);
    }

    int top()
    {
        while(q2.size()&&q1.top()==q2.top()) q1.pop(),q2.pop();
        return q1.top();
    }

    void pop()
    {
        while(q2.size()&&q1.top()==q2.top()) q1.pop(),q2.pop();
        if(q1.size()) q1.pop();
    }

    int top2()
    {
        int val=top();pop();
        int res=top();insert(val);
        return res;
    }

    int size()
    {
        return q1.size()-q2.size();
    }
}a[100010],b[100010],c;

int sz[100010],deep[100010];
int fa[100010],f[100010],fu[100010][18];
bool vis[100010],s[100010];
int n,m;

vector<int> g[100010];

void dfs(int now,int ff)
{
    deep[now]=deep[ff]+1;
    fu[now][0]=ff;
    for(int i=1;i<=17;i++)
    {
        fu[now][i]=fu[fu[now][i-1]][i-1];
    }
    for(int i=0;i<g[now].size();i++)
    {
        if(g[now][i]==ff) continue;
        dfs(g[now][i],now);
    }
}

int lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    for(int i=17;i>=0;i--)
    {
        if(deep[fu[x][i]]>=deep[y])
        {
            x=fu[x][i];
        }
    }
    if(x==y) return x;
    for(int i=17;i>=0;i--)
    {
        if(fu[x][i]!=fu[y][i])
        {
            x=fu[x][i];
            y=fu[y][i];
        }
    }
    return fu[x][0];
}

inline int dis(int x,int y)
{
    int anc=lca(x,y);
    return deep[x]+deep[y]-2*deep[anc];
}

void get_sz(int now,int ff)
{
    sz[now]=1;
    f[now]=ff;
    for(int i=0;i<g[now].size();i++)
    {
        if(vis[g[now][i]]||g[now][i]==ff) continue;
        get_sz(g[now][i],now);
        sz[now]+=sz[g[now][i]];
    }
}

int get_zx(int now,int ff)
{
    if(sz[now]==1) return now;
    int son,maxson=-1;
    for(int i=0;i<g[now].size();i++)
    {
        if(vis[g[now][i]]||g[now][i]==ff) continue;
        if(sz[g[now][i]]>maxson)
        {
            son=g[now][i];
            maxson=sz[g[now][i]];
        }
    }
    int zzx=get_zx(son,now);
    while(sz[zzx]<sz[now]-sz[zzx]) zzx=f[zzx];
    return zzx;
}

void dfs2(int now,int ff,int rtt,int zx)
{
    a[zx].insert(dis(rtt,now));
    for(int i=0;i<g[now].size();i++)
    {
        if(vis[g[now][i]]||g[now][i]==ff) continue;
        dfs2(g[now][i],now,rtt,zx);
    }
}

void build(int now,int ff)
{
    b[now].insert(0);
    fa[now]=ff;
    vis[now]=1;
    for(int i=0;i<g[now].size();i++)
    {
        if(g[now][i]==ff||vis[g[now][i]]) continue;
        get_sz(g[now][i],0);
        int zx=get_zx(g[now][i],0);
        dfs2(zx,0,now,zx);
        build(zx,now);
        int dd=dis(zx,now);
        b[now].insert(a[zx].top());
    }
    if(b[now].size()>1) c.insert(b[now].top()+b[now].top2()); 
}

void turn_on(int pos)
{
    int pre,rtt=pos;
    if(b[pos].size()>1) c.erase(b[pos].top()+b[pos].top2());
    b[pos].insert(0);
    if(b[pos].size()>1) c.insert(b[pos].top()+b[pos].top2());
    while(fa[pos])
    {
        pre=pos;
        pos=fa[pos];
        if(b[pos].size()>1) c.erase(b[pos].top()+b[pos].top2());
        if(a[pre].size()>0) b[pos].erase(a[pre].top());
        a[pre].insert(dis(rtt,pos));
        b[pos].insert(a[pre].top());
        if(b[pos].size()>1) c.insert(b[pos].top()+b[pos].top2());
    }
}

void turn_off(int pos)
{
    int pre,rtt=pos;
    if(b[pos].size()>1) c.erase(b[pos].top()+b[pos].top2());
    b[pos].erase(0);
    if(b[pos].size()>1) c.insert(b[pos].top()+b[pos].top2());
    while(fa[pos])
    {
        pre=pos;
        pos=fa[pos];
        if(b[pos].size()>1) c.erase(b[pos].top()+b[pos].top2());
        b[pos].erase(a[pre].top());
        a[pre].erase(dis(rtt,pos));
        if(a[pre].size()>0) b[pos].insert(a[pre].top());
        if(b[pos].size()>1) c.insert(b[pos].top()+b[pos].top2());
    }
}

int main()
{
    int from,to;
    n=read();
    for(int i=1;i<n;i++)
    {
        from=read();
        to=read();
        g[from].push_back(to);
        g[to].push_back(from);
    }
    dfs(1,0);
    build(1,0);
    scanf("%d",&m);
    char opt[2];
    while(m--)
    {
        scanf("%s",opt);
        if(opt[0]=='G')
        {
            printf("%d\n",c.top());
        }
        else
        {
            to=read();
            s[to]^=1;
            if(s[to]) turn_off(to);
            else turn_on(to);
        }
    }
}

你可能感兴趣的:(洛谷,点分治/动态点分治)