【BZOJ1095】捉迷藏,动态点分治

传送门
题意
给定一棵树,树上的点是黑点或白点,修改一个点的颜色或查询树上两个最远黑点的距离
原本以为动态点分治是个什么很高级的东西
原来不是像LCT一样恶心的东西啊,但也很恶心了
问了问别人才知道所谓“动态点分治”只是把点分治时得到的信息存下来,用数据结构维护一下就可以了
如果知道了什么是动态点分治,这个题目用它来做的思路还是好YY的
对于每次找到的重心 x ,它的子树集是{Vi},那么对于每一个 Vi 建一个堆,来维护其中的黑点及其深度,我们称 x 的子树Vi所代表的堆为 Q1xi
特殊的,把x自身也建一个堆出来 Q1xx
每一个 Q1 堆中的元素最多是 n 个,|Q1|大概是 O(nlogn)
(我原本以为这样的堆最多大概有 nlogn 个,但在测试样例时发现是 O(n) 数量级的,仔细想了想发现的确如此,虽然对程序没有太大影响,但这很不应该)
然后再对于每一个 x 建一个堆Q2x={max{Q1xi}}
|Q2| 大概是 O(n)
再在全局搞一个堆 Q3={max{Q2x}+secondlarge{Q2x}|x=1..n}
查询时取 Q3 max 就可以了
修改时比较麻烦
在分治过程中每个点被遍历 logn 次,所以要修改 logn Q1
具体过程可以描述为
要修改的点是 i ,i是子树集 v 里的点,现在要求改重心为x时的情况
先在 Q3 中删掉 max{Q2x}+secondlarge{Q2x}
再在 Q2v 中删去 max{Q1xv}
Q1xv 添加/删除i的信息
再在 Q2v 中加入 max{Q1xv}
最后在 Q3 中加入 max{Q2x}+secondlarge{Q2x}

mrazer:“删除操作可以再开一个辅助堆,如果辅助堆的堆顶和真正堆的堆顶相等,就都弹出来,继续找下去”

UPD
对于 Q1 堆,Yveh大爷提供了一种更容易理解的想法
实际上可以把每次寻找到的重心合起来看做一棵树,就是父重心和子重心的关系
或者称之为重心重构树
对于每个重心的所有 Q1 堆,实际上存的是重构树中它所有子树到它本身的信息

复杂度 O(mlog2n)
本来写起来就很麻烦了……
我又用了pair啥的……
然后在BZ上就MLE了,在cogs上就RE了
考虑到这样做会使辅助堆中加入很多元素但堆顶弹出来的会很少,导致M
所以一怒之下改成set
在BZ上跑了27s,在cogs上T了最后一个点(每个时限10s)
用尽浑身解数来卡常,算了一下要跑13~14s左右……
看了Po姐的博客发现其实不用pair,直接在堆中存距离就可以了
因为这些距离相等的点是可以看成等效的……
有时间再去想想神奇的括号序列做法和LCT做法

代码写得不可看
Code
set+pair

#include<cstdio>
#include<iostream>
#include<vector>
#include<set>
#include <cstdlib>
#define M 100005
using namespace std;
int n,m,G,tot,cnt;
int siz[M],mx[M],tmp[M],owner[M*15],first[M];
typedef pair<int,int>node;
vector<node>belong[M];
struct Node
{
    int poi,fa,dis;
}q[M];
struct edge{
    int v,next;
}e[M<<1];
set<node> Q1[M*15],Q2[M],Q3;
set<node>::iterator it;
bool vis[M],tp[M];
char *cp=(char *)malloc(20000000);
inline void in(int &x)
{
    for (;*cp<'0'||*cp>'9';++cp);
    for (x=0;*cp>='0'&&*cp<='9';++cp) x=x*10+(*cp)-48;
}
inline void out(int x)
{
    if (!x) return;
    out(x/10);
    putchar(x%10+48);
}
inline void add(int x,int y)
{
    e[++tot].v=y;e[tot].next=first[x];first[x]=tot;
    e[++tot].v=x;e[tot].next=first[y];first[y]=tot;
}
void find(int x,int fa)
{
    siz[x]=1;
    mx[x]=0;
    tmp[++tmp[0]]=x;
    for (int i=first[x];i;i=e[i].next)
    if (!vis[e[i].v]&&e[i].v!=fa)
        find(e[i].v,x),
        siz[x]+=siz[e[i].v],
        mx[x]=max(mx[x],siz[e[i].v]);
}
void solve(int x)
{
    tmp[0]=G=0;
    find(x,0);
    int i,z,now,head,tail,maxn;
    for (i=1;i<=tmp[0];++i)
        z=tmp[i],
        mx[z]=max(tmp[0]-siz[z],mx[z]),
        G=(!G||mx[G]>mx[z]?z:G);
    vis[G]=1;
    for (i=first[G];i;i=e[i].next)
    if (!vis[e[i].v])
    {
        owner[++cnt]=G;
        now=e[i].v,head=1,tail=1,maxn=1;
        q[1]=(Node){e[i].v,0,1};
        for (;head<=tail;++head)
        {
            x=q[head].poi;
            belong[x].push_back(make_pair(q[head].dis,cnt));
            Q1[cnt].insert(make_pair(q[head].dis,x));
            for (int j=first[x];j;j=e[j].next)
                if (e[j].v!=q[head].fa&&!vis[e[j].v])
                    q[++tail]=(Node){e[j].v,x,q[head].dis+1};
        }
        if (!Q1[cnt].empty())
            it=Q1[cnt].end(),
            --it,
            Q2[G].insert(make_pair((*it).first,cnt));
    }
    owner[++cnt]=G;
    belong[G].push_back(make_pair(0,cnt));
    Q1[cnt].insert(make_pair(0,G));
    it=Q1[cnt].end(),
    --it;
    Q2[G].insert(make_pair((*it).first,cnt));
    node a,b;
    if (!Q2[G].empty())
    {
        it=Q2[G].end();
        --it;
        a=*it;
        if (it!=Q2[G].begin())
            b=*(--it),
            Q3.insert(make_pair(a.first+b.first,G));
    }
    x=G;
    for (i=first[x];i;i=e[i].next)
        if (!vis[e[i].v]) solve(e[i].v);
}
main()
{
    fread(cp,1,20000000,stdin);
    in(n);
    int v,t,x,i,y;
    for (i=1;i<n;++i)
        in(x),in(y),
        add(x,y);
    tot=n;
    solve(1);
    node a,b;
    for (in(m);m;--m)
    {
        ++cp;
        while (*cp!='G'&&*cp!='C') ++cp;
        if (*cp=='G')
        {
            if (!tot) puts("-1");
            if (tot==1) puts("0");
            if (tot>1)
                it=Q3.end(),
                --it,
                out((*it).first),
                putchar('\n');
        }
        else
        {
            in(x);
            tp[x]^=1;
            tot+=(tp[x]?-1:1);
            for (i=0;i<belong[x].size();++i)
            {
                v=belong[x][i].second;
                t=owner[v];
                if (!Q2[t].empty())
                {
                    it=Q2[t].end();
                    --it;
                    a=*it;
                    if (it!=Q2[t].begin())
                        b=*(--it),
                        Q3.erase(make_pair(a.first+b.first,t));
                }
                if (!Q1[v].empty())
                    it=Q1[v].end(),
                    --it,
                    Q2[t].erase(make_pair((*it).first,v));
                if (tp[x]) Q1[v].erase(make_pair(belong[x][i].first,x));
                else Q1[v].insert(make_pair(belong[x][i].first,x));
                if (!Q1[v].empty())
                    it=Q1[v].end(),
                    --it,
                    Q2[t].insert(make_pair((*it).first,v));
                if (!Q2[t].empty())
                {
                    it=Q2[t].end();
                    --it;
                    a=*it;
                    if (it!=Q2[t].begin())
                        b=*(--it),
                        Q3.insert(make_pair(a.first+b.first,t));
                }
            }
        }
    }
}

Code

#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include <cstdlib>
#define M 100005
using namespace std;
int n,G,tot,cnt;
int siz[M],mx[M],tmp[M],owner[M*2];
vector<int> e[M];
typedef pair<int,int>node;
vector<node>belong[M];
struct Node
{
    int poi,fa,dis;
}q[M];
priority_queue<int> Q1[2][M*2],Q2[2][M*2],Q3[2];
//Q1存子树节点及距离 Q2存Q1编号及距离 Q3存各个重心及距离 
bool vis[M],tp[M];
int in()
{
    char ch=getchar();int t=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') t=t*10+ch-48,ch=getchar();
    return t;
}
void find(int x,int fa)
{
    siz[x]=1;
    mx[x]=0;
    tmp[++tmp[0]]=x;
    for (int v,i=0;i<e[x].size();++i)
    {
        v=e[x][i];
        if (vis[v]||v==fa) continue;
        find(v,x);
        siz[x]+=siz[v];
        mx[x]=max(mx[x],siz[v]);
    }
}
void solve(int x)
{
    tmp[0]=G=0;
    find(x,0);
    for (int i=1;i<=tmp[0];++i)
        mx[tmp[i]]=max(tmp[0]-siz[tmp[i]],mx[tmp[i]]),
        G=(!G||mx[G]>mx[tmp[i]]?tmp[i]:G);
    vis[G]=1;
    for (int v,i=0;i<e[G].size();++i)
    {
        v=e[G][i];
        if (vis[v]) continue;
        owner[++cnt]=G;
        int now=v,head=1,tail=1,mx=1;
        q[1]=(Node){v,0,1};
        for (;head<=tail;++head)
        {
            v=q[head].poi;
            belong[v].push_back(make_pair(q[head].dis,cnt));
            Q1[0][cnt].push(q[head].dis);
            for (int j=0;j<e[v].size();++j)
                if (e[v][j]!=q[head].fa&&!vis[e[v][j]])
                    q[++tail]=(Node){e[v][j],v,q[head].dis+1};
        }
        if (!Q1[0][cnt].empty())
            Q2[0][G].push(Q1[0][cnt].top());
    }
    owner[++cnt]=G;
    belong[G].push_back(make_pair(0,cnt));
    Q1[0][cnt].push(0);
    Q2[0][G].push(Q1[0][cnt].top());
    int a,b;
    if (!Q2[0][G].empty())
    {
        a=Q2[0][G].top();
        Q2[0][G].pop();
        if (!Q2[0][G].empty())
            b=Q2[0][G].top(),
            Q3[0].push(a+b);
        Q2[0][G].push(a);
    }
    x=G;
    for (int v,i=0;i<e[x].size();++i)
    {
        v=e[x][i];
        if (vis[v]) continue;
        solve(v);
    }
}
void Q1_clr(int v)
{
    while (!Q1[0][v].empty()&&!Q1[1][v].empty()&&Q1[0][v].top()==Q1[1][v].top())
        Q1[0][v].pop(),Q1[1][v].pop();
}
void Q2_clr(int v)
{
    while (!Q2[0][v].empty()&&!Q2[1][v].empty()&&Q2[0][v].top()==Q2[1][v].top())
        Q2[0][v].pop(),Q2[1][v].pop();
}

void Q3_clr()
{
    while (!Q3[1].empty()&&!Q3[0].empty()&&Q3[1].top()==Q3[0].top())
        Q3[0].pop(),Q3[1].pop(); 
}
main()
{
    n=in();tot=n;
    for (int x,y,i=1;i<n;++i)
        x=in(),y=in(),
        e[x].push_back(y),
        e[y].push_back(x);
    solve(1);
    for (int m=in();m;--m)
    {
        char ch=getchar();
        while (ch!='G'&&ch!='C') ch=getchar();
        if (ch=='G')
        {
            if (!tot) puts("-1");
            if (tot==1) puts("0");
            if (tot>1)
                Q3_clr(),
                printf("%d\n",Q3[0].top());
        }
        else
        {
            int x=in();
            tp[x]^=1;
            if (tp[x]) --tot;
            else ++tot;
            for (int v,t,i=0;i<belong[x].size();++i)
            {
                v=belong[x][i].second;
                t=owner[v];
                Q2_clr(t);
                if (!Q2[0][t].empty())
                {
                    int a,b;
                    a=Q2[0][t].top();
                    Q2[0][t].pop();
                    Q2_clr(t);
                    if (!Q2[0][t].empty())
                        b=Q2[0][t].top(),
                        Q3[1].push(a+b),
                        Q3_clr();
                    Q2[0][t].push(a);
                }
                Q1_clr(v);
                if (!Q1[0][v].empty())
                {
                    Q2[1][t].push(Q1[0][v].top());
                    Q2_clr(t);
                }
                Q1[tp[x]][v].push(belong[x][i].first);
                Q1_clr(v);
                if (!Q1[0][v].empty())
                {
                    Q2[0][t].push(Q1[0][v].top());
                    Q2_clr(t);
                }
                Q2_clr(t);
                if (!Q2[0][t].empty())
                {
                    int a,b;
                    a=Q2[0][t].top();
                    Q2[0][t].pop();
                    Q2_clr(t);
                    if (!Q2[0][t].empty())
                        b=Q2[0][t].top(),
                        Q3[0].push(a+b),
                        Q3_clr();
                    Q2[0][t].push(a);
                }               
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值