3786: 星系探索 splay维护入栈出栈序

物理学家小C在研究一个星系,星系由n个星球组成,存在依赖关系且无环。通过使用splay树维护星球间的依赖关系及能量系数的变化,解决在星球依赖关系改变与能量变化的情况下,如何计算能量收集器从特定星球出发到主星球所收集的能量总和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description
物理学家小C的研究正遇到某个瓶颈。

他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球。主星球没有依赖星球。

我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关系具有传递性,即若星球a依赖星球b,星球b依赖星球c,则有星球a依赖星球c.

对于这个神秘的星系中,小C初步探究了它的性质,发现星球之间的依赖关系是无环的。并且从星球a出发只能直接到达它的依赖星球b.

每个星球i都有一个能量系数wi.小C想进行若干次实验,第i次实验,他将从飞船上向星球di发射一个初始能量为0的能量收集器,能量收集器会从星球di开始前往主星球,并收集沿途每个星球的部分能量,收集能量的多少等于这个星球的能量系数。

但是星系的构成并不是一成不变的,某些时刻,星系可能由于某些复杂的原因发生变化。

有些时刻,某个星球能量激发,将使得所有依赖于它的星球以及他自己的能量系数均增加一个定值。还有可能在某些时刻,某个星球的依赖星球会发生变化,但变化后依然满足依赖关系是无环的。

现在小C已经测定了时刻0时每个星球的能量系数,以及每个星球(除了主星球之外)的依赖星球。接下来的m个时刻,每个时刻都会发生一些事件。其中小C可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。

题解:

挺不错的一个套路题。如果没有换父亲的操作,那么树链剖分就能做了,有的话我们就要用一个东西——splay维护入栈出栈序。其实这个东西和dfs序是有一点类似的,就是在一个点进入dfs的时候(入栈)在当前位置加入一个正的权值,在结束dfs(出栈)时在当前位置加入一个负的权值,那么求一个点x到根的权值和,就可以求1到x入栈位置的权值和,因为此时只有x到根路径上的点在栈中。对于换父亲操作,只需要splay区间移动操作就可以了。注意把一段区间提取出来时不能简单的把l-1和r+1之间的提取,而需要找到排名在其前或后一位的位置。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=200010;
#define LL long long
struct Node
{
    int fa,son[2],d,type,size;
    LL tag,sv,v,st;
}tr[Maxn<<1];//sv子树和 v节点权值 type节点类型 st子树节点类型和 
int n;LL w[Maxn];
int tot=0,root;
void work(int x,LL v)
{
    tr[x].v+=tr[x].type*v;
    tr[x].sv+=tr[x].st*v;
    tr[x].tag+=v;
}
void up(int x)
{
    int lc=tr[x].son[0],rc=tr[x].son[1];
    tr[x].sv=tr[x].v;tr[x].st=tr[x].type;tr[x].size=1;
    if(lc)tr[x].sv+=tr[lc].sv,tr[x].st+=tr[lc].st,tr[x].size+=tr[lc].size;
    if(rc)tr[x].sv+=tr[rc].sv,tr[x].st+=tr[rc].st,tr[x].size+=tr[rc].size;
}
void down(int x)
{
    if(tr[x].tag)
    {
        LL t=tr[x].tag;
        int lc=tr[x].son[0],rc=tr[x].son[1];
        if(lc)work(lc,t);
        if(rc)work(rc,t);
        tr[x].tag=0;
    }
}
void rotate(int x)
{
    int y=tr[x].fa,z=tr[y].fa,w=(tr[y].son[0]==x);
    tr[y].son[w^1]=tr[x].son[w];if(tr[x].son[w])tr[tr[x].son[w]].fa=y;
    tr[z].son[tr[z].son[1]==y]=x;tr[x].fa=z;
    tr[x].son[w]=y;tr[y].fa=x;
    up(y),up(x);
}
int sta[Maxn],top;
void update(int x)
{
    top=0;
    while(x)sta[++top]=x,x=tr[x].fa;
    while(top)down(sta[top--]);
}
void splay(int x,int rt)
{
    update(x);
    while(tr[x].fa!=rt)
    {
        int y=tr[x].fa,z=tr[y].fa;
        if(z==rt)rotate(x);
        else rotate(((tr[z].son[1]==y)==(tr[y].son[1]==x))?y:x),rotate(x);
    }
    if(!rt)root=x;
}
int find1(int rank)
{
    int x=root;
    while(1)
    {
        int lc=tr[x].son[0],rc=tr[x].son[1];
        int ls=((lc)?tr[lc].size:0);
        if(ls+1==rank)return x;
        else if(rank<=ls)x=lc;
        else rank-=(ls+1),x=rc;
    }
}
int find2(int x)
{
    if(x==root)return tr[tr[x].son[0]].size+1;
    if(tr[tr[x].fa].son[1]==x)return find2(tr[x].fa)+tr[tr[x].son[0]].size+1;
    return find2(tr[x].fa)-tr[tr[x].son[1]].size-1;
}
struct Edge{int y,next;}e[Maxn];
int len=0,last[Maxn];
void ins(int x,int y){int t=++len;e[t].y=y;e[t].next=last[x];last[x]=t;}
int dfn=1,L[Maxn],R[Maxn];
void dfs(int x)
{
    L[x]=++dfn;tr[dfn].v=w[x],tr[dfn].type=1,tr[dfn].d=dfn,tr[dfn].tag=0;
    for(int i=last[x];i;i=e[i].next)dfs(e[i].y);
    R[x]=++dfn;tr[dfn].v=-w[x],tr[dfn].type=-1,tr[dfn].d=dfn,tr[dfn].tag=0;
}
void build(int l,int r,int Fa)
{
    if(l>r)return;
    int mid=l+r>>1;
    tr[mid].fa=Fa;
    if(Fa)tr[Fa].son[tr[mid].d>tr[Fa].d]=mid;
    build(l,mid-1,mid);build(mid+1,r,mid);
    up(mid);
}
void workQ()
{
    int x;
    scanf("%d",&x);
    splay(L[x],0);
    printf("%lld\n",tr[L[x]].v+tr[tr[L[x]].son[0]].sv);
}
void workF()
{
    int p;LL q;
    scanf("%d%lld",&p,&q);
    int t1=find1(find2(L[p])-1),t2=find1(find2(R[p])+1);
    splay(t1,0);splay(t2,t1);
    work(tr[t2].son[0],q);
}
void workC()
{
    int x,y;
    scanf("%d%d",&x,&y);
    int t1=find1(find2(L[x])-1),t2=find1(find2(R[x])+1);
    splay(t1,0);splay(t2,t1);
    down(t1);down(t2);
    int tmp=tr[t2].son[0];
    tr[tmp].fa=0;
    tr[t2].son[0]=0;
    up(t2);up(t1);
    t1=find1(find2(R[y])-1);
    splay(t1,0);splay(R[y],t1);
    down(t1);down(R[y]);
    tr[tmp].fa=R[y];
    tr[R[y]].son[0]=tmp;
    up(R[y]);up(t1);
}
int main()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        int father;
        scanf("%d",&father);
        ins(father,i);
    }
    for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
    dfs(1);
    tr[1].d=tr[1].sv=tr[1].st=0;
    tr[dfn+1].d=dfn+1;tr[dfn+1].sv=tr[dfn+1].st=0;
    root=(dfn+1)>>1;
    build(1,dfn+1,0);
    int Q;
    scanf("%d",&Q);
    while(Q--)
    {
        char op[3];
        scanf("%s",op);
        if(op[0]=='Q')workQ();
        else if(op[0]=='F')workF();
        else workC();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值