[SPOJ2939]Qtree5

[SPOJ2939]Qtree5

Tags:题解


题意

链接
给你\(n\)个节点的黑白树,初始全黑。每次可以翻转某点颜色,或查询距离某点最近的白点的距离。\(n\le 10^5\)。强制LCT,不准动点分。

题解

这题神了。
LCT中每个splay的中序遍历是一条直上直下的链。

维护什么呢?

\(splay\)中的\(x\)点维护\(x\)\(splay\)子树中,在原树上深度最深和深度最浅的点、到达\(x\)\(splay\)子树中的点在原图上的子树中、最近的白点的距离。分别记为\(lm、rm\)

怎么维护呢?

需要维护原图信息那么开一个\(set\)维护虚子树
深度最浅的点是x的最左儿子,它的最近白点距离可能由以下几方面

  • 左子树的答案
  • 左子树的\(siz\)(若\(x\)为白点)
  • 左子树的\(siz+1+\)右子树的答案

同理维护最深的点。

怎么查呢?

\(x\)\(Access\)这样它就是这条链上最深的点了。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<set>
#define lc t[x].ch[0]
#define rc t[x].ch[1]
using namespace std;
const int N=2e5+10,inf=1e9;
int n,q,head[N],cnt;
struct edge{int next,to;}a[N];
struct Node{int ch[2],siz,lm,rm,col,fa;multiset<int> s;}t[N];
multiset<int>::iterator it;
void link(int x,int y) {a[++cnt]=(edge){head[x],y};head[x]=cnt;}
int isroot(int x) {return t[t[x].fa].ch[0]!=x&&t[t[x].fa].ch[1]!=x;}
int fin(int x) {return t[x].s.empty()?inf:*t[x].s.begin();}
void pushup(int x)
{
    t[x].siz=t[lc].siz+t[rc].siz+1;
    t[x].lm=min(t[lc].lm,t[lc].siz+min(t[x].col?0:inf,min(fin(x),t[rc].lm+1)));
    t[x].rm=min(t[rc].rm,t[rc].siz+min(t[x].col?0:inf,min(fin(x),t[lc].rm+1)));
}
void rotate(int x)
{
    int y=t[x].fa,z=t[y].fa;
    int k=t[y].ch[1]==x;
    if(!isroot(y)) t[z].ch[t[z].ch[1]==y]=x; t[x].fa=z;
    t[y].ch[k]=t[x].ch[k^1];                 t[t[x].ch[k^1]].fa=y;
    t[x].ch[k^1]=y;                          t[y].fa=x;
    pushup(y);
}
void splay(int x)
{
    while(!isroot(x))
    {
        int y=t[x].fa,z=t[y].fa;
        if(!isroot(y)) (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
        rotate(x);
    }
    pushup(x);
}
void Access(int x)
{
    for(int y=0;x;y=x,x=t[x].fa)
    {
        splay(x);t[x].s.insert(t[rc].lm+1);
        rc=y;it=t[x].s.lower_bound(t[rc].lm+1);
        if(it!=t[x].s.end()&&*it==t[rc].lm+1) t[x].s.erase(it);
        pushup(x);
    }
}
void dfs(int x,int fr)
{
    for(int i=head[x];i;i=a[i].next)
    {
        int R=a[i].to;if(fr==R) continue;
        t[R].fa=x;t[x].s.insert(inf+1);dfs(R,x);
    }
    pushup(x);
}
int main()
{
    cin>>n;
    t[0].lm=t[0].rm=inf+1;
    for(int i=1,x,y;i<n;i++)
        scanf("%d%d",&x,&y),link(x,y),link(y,x);
    cin>>q;dfs(1,0);
    for(int i=1;i<=q;i++)
    {
        int op,x;scanf("%d%d",&op,&x);
        if(op==0)
        {
            Access(x);splay(x);
            t[x].col^=1;pushup(x);
        }
        else
        {
            Access(x);splay(x);
            printf("%d\n",t[x].rm>n?-1:t[x].rm);
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/xzyxzy/p/10347633.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值