【BZOJ-3786】星系探索 Splay + DFS序

3786: 星系探索

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 647  Solved: 212
[Submit][Status][Discuss]

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可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。

Input

第一行一个整数n,表示星系的星球数。

接下来n-1行每行一个整数,分别表示星球2-n的依赖星球编号。

接下来一行n个整数,表示每个星球在时刻0时的初始能量系数wi.

接下来一行一个整数m,表示事件的总数。

事件分为以下三种类型。

(1)"Q di"表示小C要开始一次实验,收集器的初始位置在星球di.

(2)"C xi yi"表示星球xi的依赖星球变为了星球yi.

(3)"F pi qi"表示星球pi能量激发,常数为qi.

Output

对于每一个事件类型为Q的事件,输出一行一个整数,表示此次实验的收集器最终能量。

Sample Input

3
1
1
4 5 7
5
Q 2
F 1 3
Q 2
C 2 3
Q 2

Sample Output

9
15
25

HINT

n<=100000,m<=300000,1<di,xi<=n,wi,qi<=100000.保证操作合法。

Source

By 佚名上传

Solution

子树有关修改,显然不能用Link-Cut-Tree...

考虑用DFS序,但换根操作显然不能用线段树维护,所以用支持分裂合并的Splay!
我们把DFS序中的入栈出栈分开,在一个点入栈时权值为+val[x],出栈时权值为-val[x],这样对于他的子树,影响显然会抵消

这样的话,查询点x到跟的$\sum_{x}^{root}val[x]$,就相当于前缀和查询$\sum_{pl[1]}^{pl[x]}val[x]$

子树修改,就是打标记。

换根操作,把x换到y,先把x的子树区间分裂开,再把y的子树区间提取,然后把x的子树区间连到y的子树区间最左即可

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<stack>
using namespace std;
inline int read()
{
    int x=0; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x;
}
#define LL long long
#define MAXN 200010
int N,M,power[MAXN],val[MAXN];
struct EdgeNode{int next,to,d;}edge[MAXN<<1];
int head[MAXN],cnt=1;
inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
int pl[MAXN],dfn,pr[MAXN],pre[MAXN],del[MAXN];
void DFS(int now,int last)
{
    pl[now]=++dfn; pre[dfn]=now; del[dfn]=1;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=last)
            DFS(edge[i].to,now);
    pr[now]=++dfn; del[dfn]=-1;
}
namespace SplayTree
{

    int fa[MAXN],son[MAXN][2],sz,root,ln[MAXN],rn[MAXN],pos[MAXN];
    LL sum[MAXN],tag[MAXN];
    //ln记录左的数量,rn记录右的数量,tag标记
    #define ls(x) son[x][0]
    #define rs(x) son[x][1]
    #define INF 1000100

    inline void Update(int now)
    {
        sum[now]=sum[ls(now)]+sum[rs(now)]+(LL)val[now];
        ln[now]=ln[ls(now)]+ln[rs(now)]+(del[now]==1);
        rn[now]=rn[ls(now)]+rn[rs(now)]+(del[now]==-1);
    }
    inline void paint(int now,LL D) {if (now) val[now]+=D*del[now],sum[now]+=D*(ln[now]-rn[now]),tag[now]+=D;}
    inline void PushDown(int now)
    {
        if (!tag[now]) return;
        LL D=tag[now]; tag[now]=0;
        if (ls(now)) paint(ls(now),D);
        if (rs(now)) paint(rs(now),D);
    }
    void print(int now)
    {
        PushDown(now);
        if (val[now]!=INF && val[now]!=-INF)
            printf("ID=%d ---%d  [%d %d]  %d   %I64d   %d   %d   %d  %I64d\n",
            now,fa[now],ls(now),rs(now),val[now],sum[now],ln[now],rn[now],del[now],tag[now]);
           if (son[now][0]) print(son[now][0]);
        if (son[now][1]) print(son[now][1]);
    }
    inline bool Right(int now) {return son[fa[now]][1]==now;}
    inline void rotate(int now)
    {
        PushDown(fa[now]); PushDown(now);
        int f=fa[now],gf=fa[f],wh=Right(now);
        son[f][wh]=son[now][wh^1]; fa[son[f][wh]]=f;
        fa[f]=now; son[now][wh^1]=f; fa[now]=gf;
        if (gf) son[gf][son[gf][1]==f]=now;
        Update(f); Update(now);
    }
    inline void splay(int now,int tar)
    {
        for (int f; (f=fa[now])!=tar; rotate(now))
            if (fa[f]!=tar) rotate(Right(now)==Right(f)? f:now);
        if (!tar) root=now;
    }
    inline int fmin(int x) {while (son[x][0]) x=son[x][0]; return x;}
    inline int fmax(int x) {while (son[x][1]) x=son[x][1]; return x;}
    inline void Split(int l,int r)
    {
        splay(l,0); int x=fmax(ls(root));
        splay(r,0); int y=fmin(rs(root));
        splay(x,0); splay(y,x);
    }
    inline LL Query(int pos) {Split(pl[1],pl[pos]); return sum[ls(rs(root))];}
    inline void Change(int x,int D)
    {
        Split(pl[x],pr[x]);
        int rt=son[rs(root)][0];
        sum[rt]+=(LL)(ln[rt]-rn[rt])*D; val[rt]+=D*del[rt]; tag[rt]+=D;
        Update(rs(root)); Update(root);
    }
    inline void BuildTree(int l,int r,int last)
    {
        if (r<l) return;
        int mid=(l+r)>>1,now=mid;
        fa[now]=last; son[last][now>last]=now;
        if (l==r)
            {sum[now]=val[now]; ln[now]=del[now]==1; rn[now]=1-ln[now]; return;}
        BuildTree(l,mid-1,now); BuildTree(mid+1,r,now);
        Update(now);
    }
    inline void Init() {BuildTree(1,(N<<1)+2,0); root=(1+(N<<1)+2)>>1;}
    inline void MakeRoot(int now,int father)
    {
        Split(pl[now],pr[now]);
        int rt=ls(rs(root));
        fa[ls(rs(root))]=0;
        son[rs(root)][0]=0;
        Update(rs(root)),Update(root);
        splay(pl[father],0); splay(fmin(rs(root)),root);
        son[rs(root)][0]=rt; fa[rt]=rs(root);
        Update(rs(root)),Update(root);
    }
}
using namespace SplayTree;
int main()
{
    N=read();
    for (int i=2; i<=N; i++) InsertEdge(i,read());
    dfn=1; DFS(1,0);
    for (int i=1; i<=N; i++) power[i]=read();
//    for (int i=1; i<=N; i++) printf("id=%d [%d , %d]\n",pre[i],pl[pre[i]],pr[pre[i]]);
    for (int i=1; i<=N; i++) val[pl[i]]+=power[i],val[pr[i]]-=power[i];
//    for (int i=1; i<=dfn; i++) printf("%d ",val[i]); puts("");
    M=read();
    SplayTree::Init();
    while (M--)
        {
//            print(root);
            char opt[5]; scanf("%s",opt); int x,y;
            if (opt[0]==‘Q‘) x=read(),printf("%I64d\n",SplayTree::Query(x));
            if (opt[0]==‘C‘) x=read(),y=read(),SplayTree::MakeRoot(x,y);
            if (opt[0]==‘F‘) x=read(),y=read(),SplayTree::Change(x,y);
        }
    return 0;
}

一天前..我:char哥,要是DFS序不用线段树,用Splay的话,不就可以支持子树交换XXX了吗...    char哥:...woc!这么科学....

于是跟char哥连坐,写了一天Splay,然后这道题昨天没调出来...

终于写出来了...40s时限41s  AC....

我:我一定要压到40s以内!    .....  我:当我没说;

我:char哥,你有什么压常数的技巧么?   char哥:重写,你这程序已经没救了!......

本机加O2优化:

时间: 2024-10-13 14:14:16

【BZOJ-3786】星系探索 Splay + DFS序的相关文章

bzoj 3786 星系探索 (splay+dfs序)

题目大意:给你一棵树,支持一下三种操作 1.获取某节点到根节点的路径上所有节点的权值和 2.更换某棵子树的父亲 3.某子树内所有节点的权值都增加一个值w 当时想到了splay维护dfs序,查完题解发现思路是对的,然后我就写了足足6个小时才A st[x]代表入栈时间,ed[x]代表出栈时间 对于第一个操作,每个树上节点在splay中都有两个位置,分别对应入栈出栈序,然后把入栈的点权*1,出栈点权*-1,就可以避免其它子树干扰了 接着查询到根节点的路径权值和呢,splay把1~st[x]整个序列都扔

BZOJ 3786 星系探索 Splay维护树的入栈出栈序

题目大意:给出一棵树,要求有以下这些操作:1.求出一个节点到根的点权和.2.将一个节点的父亲改变.3.将一个子树中的每一个节点都加上一个权值. 思路:LCT就不用想了,因为有子树操作.然后就是一个很神奇的东西了,就是Splay维护树的入栈出栈序.这个玩应是做了这个题之后才知道的.但是感觉真的很dio. 首先,我们先按照题意,将树建出来.然后从根开始深搜,这样一个点进入DFS函数和出DFS函数的时候就会有两个时间点,就是入栈的时间和出栈的时间.然后利用Splay维护一个序列,就是入栈出栈的顺序.在

[BZOJ 3786] 星系探索 Splay维护入栈出栈序

题意 给定一棵 n 个节点, 点有点权, 以 1 为根的有根树. m 次操作: ① 查询点 d 到根的点权之和. ② 将 x 及其子树截出来, 作为 y 的儿子. ③ 将以 p 为根的子树的点权增加 q . $n \le 100000$ . 实现 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include <vector>

bzoj 3786 星系探索 dfs+splay

[BZOJ3786]星系探索 Description 物理学家小C的研究正遇到某个瓶颈. 他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球.主星球没有依赖星球. 我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关系具有传递性,即若星球a依赖星球b,星球b依赖星球c,则有星球a依赖星球c. 对于这个神秘的星系中,小C初步探究了它的性质,发现星球之间的依赖关系是无环的.并且从星球a出发只

BZOJ 3786 星系探索

Description 物理学家小C的研究正遇到某个瓶颈. 他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球.主星球没有依赖星球. 我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关系具有传递性,即若星球a依赖星球b,星球b依赖星球c,则有星球a依赖星球c. 对于这个神秘的星系中,小C初步探究了它的性质,发现星球之间的依赖关系是无环的.并且从星球a出发只能直接到达它的依赖星球b. 每

bzoj3786 星际探索 splay dfs序

这道题 首先 因为他求的是当前点到根节点的路径和 我们可以将题目转换为括号序列的写法 将点拆为左括号以及右括号 左括号为正 右括号为负 这样题目就变为了求前缀和了 如果一个点是这个点的子树 那么他的左右括号就一定包含在所求区间里 会被抵消掉而不影响结果. 这样我们可以利用dfs序建树 操作为区间加 单点修改 树的合并以及分裂 一起区间和 当然区间加因为我们维护的是括号序列 所以区间加的时候我们就要将左括号加w而右括号减w 但是我们因此我们还要记录这个点子树及其本身的左右括号差 区间加就加上左括号

BZOJ 3786 星系探索 DFS序+Splay

题目大意:给定一棵有根树,提供下列操作: 1.询问某个点到根路径上的点权和 2.修改某个点的父亲,保证修改之后仍然是一棵树 3.将某个点所在子树的所有点权加上一个值 子树修改,LCT明显是搞不了了,在想究竟会不会有人去写自适应Top-Tree-- 首先我们DFS搞出这棵树的入栈出栈序 然后入栈为正出栈为负 那么一个点到根的路径上的点权和就是从根节点的入栈位置到这个点的入栈位置的和 子树修改就记录某段序列中有多少入栈和多少出栈,然后根据这个对这棵子树所在序列修改即可 那么换父亲操作呢?显然可以用S

bzoj3786星系探索 splay

3786: 星系探索 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1314  Solved: 425[Submit][Status][Discuss] Description 物理学家小C的研究正遇到某个瓶颈. 他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球.主星球没有依赖星球. 我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关

Bzoj3786: 星系探索——Splay

题面  Bzoj3786 解析  上课讲稿上的例题 这道题是套路题,是括号序的应用,进入节点时打上$+1$标记, 退出时打上$-1$标记,这个是作为点权的系数 先看操作2, 需要更改父节点,就是把一段区间提取出来,插入另一个地方,显然可以用Splay维护,先提取区间,再把新父亲的$+1$点旋转至根,把区间挂在根的后继的左儿子上,再把这个节点旋转至根,以更新信息 对于操作1,求点到根的路径和,就是求括号序列的前缀和,该点对应的$+1$点或$-1$点的前缀和都可,我是把$-1$的点旋转至根,答案就是