Bzoj3786: 星系探索——Splay

题面

   Bzoj3786

解析

   上课讲稿上的例题

  这道题是套路题,是括号序的应用,进入节点时打上$+1$标记, 退出时打上$-1$标记,这个是作为点权的系数
  先看操作2, 需要更改父节点,就是把一段区间提取出来,插入另一个地方,显然可以用Splay维护,先提取区间,再把新父亲的$+1$点旋转至根,把区间挂在根的后继的左儿子上,再把这个节点旋转至根,以更新信息

  对于操作1,求点到根的路径和,就是求括号序列的前缀和,该点对应的$+1$点或$-1$点的前缀和都可,我是把$-1$的点旋转至根,答案就是根的左儿子的子树和,因此需要维护子树和

  最后还有操作3,显然打标记,问题在对于子树和的改变, 记该子树中$+1$标记有$cnt1$个,$-1$标记有$cnt_1$个,则子树和需要加$(cnt1 - cnt_1) * delta$,因此需要维护子树中$+1$与$-1$的个数

 代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 100004;

template<class T> void read(T &re)
{
    re=0;
    T sign=1;
    char tmp;
    while((tmp=getchar())&&(tmp<‘0‘||tmp>‘9‘)) if(tmp==‘-‘) sign=-1;
    re=tmp-‘0‘;
    while((tmp=getchar())&&(tmp>=‘0‘&&tmp<=‘9‘)) re=(re<<3)+(re<<1)+(tmp-‘0‘);
    re*=sign;
}

int n, m, root, cnt, a[maxn], fir[maxn], sec[maxn];
vector<int> G[maxn];

struct spaly_tree{
    int s[2], fa, val, add, cnt1, cnt_1, fl[2];
    ll sum;
}tr[maxn<<1];

void dfs(int x)
{
    fir[x] = ++cnt;
    tr[cnt].fl[1] = 1;
    tr[cnt].val = a[x];
    tr[cnt].sum = (ll)a[x];
    for(unsigned int i = 0; i < G[x].size(); ++i)
    {
        int id = G[x][i];
        dfs(id);
    }
    sec[x] = ++cnt;
    tr[cnt].fl[0] = 1;
    tr[cnt].val = -a[x];
    tr[cnt].sum = (ll)(-a[x]);
}

void build(int l, int r, int ff)
{
    int mid = (l + r)>>1;
    if(l < mid)
        build(l, mid - 1, mid);
    if(mid < r)
        build(mid + 1, r, mid);
    if(ff)
        tr[ff].s[ff < mid] = mid;
    int ls = tr[mid].s[0], rs = tr[mid].s[1];
    tr[mid].fa = ff;
    tr[mid].cnt1 = tr[ls].cnt1 + tr[rs].cnt1 + tr[mid].fl[1];
    tr[mid].cnt_1 = tr[ls].cnt_1 + tr[rs].cnt_1 + tr[mid].fl[0];
    tr[mid].sum += tr[ls].sum + tr[rs].sum;
}

void spread(int x)
{
    if(tr[x].add)
    {
        int ls = tr[x].s[0], rs = tr[x].s[1];
        if(ls)
        {
            tr[ls].sum += 1LL * (tr[ls].cnt1 - tr[ls].cnt_1) * tr[x].add;
            tr[ls].val += (tr[ls].fl[1]? tr[x].add: -tr[x].add);
            tr[ls].add += tr[x].add;
        }
        if(rs)
        {
            tr[rs].sum += 1LL * (tr[rs].cnt1 - tr[rs].cnt_1) * tr[x].add;
            tr[rs].val += (tr[rs].fl[1]? tr[x].add: -tr[x].add);
            tr[rs].add += tr[x].add;
        }
        tr[x].add = 0;
    }
}

void update(int x)
{
    int ls = tr[x].s[0], rs = tr[x].s[1];
    tr[x].sum = tr[ls].sum + tr[rs].sum + 1LL * tr[x].val;
    tr[x].cnt1 = tr[ls].cnt1 + tr[rs].cnt1 + tr[x].fl[1];
    tr[x].cnt_1 = tr[ls].cnt_1 + tr[rs].cnt_1 + tr[x].fl[0];
}

void Rotate(int x)
{
    int y = tr[x].fa, z = tr[y].fa, k = (tr[y].s[1] == x), w = (tr[z].s[1] == y), son = tr[x].s[k^1];
    spread(y);spread(x);
    tr[y].s[k] = son;tr[son].fa = y;
    tr[x].s[k^1] = y;tr[y].fa = x;
    tr[z].s[w] = x;tr[x].fa = z;
    update(y);update(x);
}

void Splay(int x, int to)
{
    int y, z;
    while(tr[x].fa != to)
    {
        y = tr[x].fa;
        z = tr[y].fa;
        if(z != to)
            Rotate((tr[y].s[0] == x) ^ (tr[z].s[0] == y)? x: y);
        Rotate(x);
    }
    if(!to)
        root = x;
}

int Findpre()
{
    spread(root);
    int now = tr[root].s[0];
    while(tr[now].s[1])
    {
        spread(now);
        now = tr[now].s[1];
    }
    spread(now);
    return now;
}

int Findnxt()
{
    spread(root);
    int now = tr[root].s[1];
    while(tr[now].s[0])
    {
        spread(now);
        now = tr[now].s[0];
    }
    spread(now);
    return now;
}

void Remove(int x, int y)
{
    Splay(fir[x], 0);
    int pre = Findpre();
    Splay(sec[x], 0);
    int nxt = Findnxt();
    Splay(pre, 0);
    Splay(nxt, pre);
    int now = tr[nxt].s[0];
    tr[nxt].s[0] = 0;
    update(nxt);update(pre);
    Splay(fir[y], 0);
    nxt = Findnxt();
    tr[now].fa = nxt;
    tr[nxt].s[0] = now;
    Splay(now, 0);
}

int main()
{
    read(n);
    for(int i = 1; i < n; ++i)
    {
        int x;
        read(x);
        G[x].push_back(i+1);
    }
    for(int i = 1; i <= n; ++i)
        read(a[i]);
    cnt = 1;
    dfs(1);
    cnt ++;
    build(1, cnt, 0);
    tr[0].s[1] = root = (1 + cnt)>>1;
    read(m);
    for(int i = 1; i <= m; ++i)
    {
        char opt[3];
        scanf("%s", opt);
        if(opt[0] == ‘Q‘)
        {
            int x;
            read(x);
            Splay(sec[x], 0);
            printf("%lld\n", tr[tr[root].s[0]].sum);
        }
        else if(opt[0] == ‘C‘)
        {
            int x, y;
            read(x);read(y);
            Remove(x, y);
        }
        else
        {
            int x, y;
            read(x);read(y);
            if(!y)    continue;
            Splay(fir[x], 0);
            int pre = Findpre();
            Splay(sec[x], 0);
            int nxt = Findnxt();
            Splay(pre, 0);
            Splay(nxt, pre);
            int p = tr[nxt].s[0];
            tr[p].sum += 1LL * (tr[p].cnt1 - tr[p].cnt_1) * y;
            tr[p].val += (tr[p].fl[1]? y: -y);
            tr[p].add += y;
            update(nxt);update(pre);
        }
    }
    return 0;
}

  

原文地址:https://www.cnblogs.com/Joker-Yza/p/11366884.html

时间: 2024-10-10 04:07:45

Bzoj3786: 星系探索——Splay的相关文章

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.此外,依赖关

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

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

[BZOJ3786]星系探索(伪ETT)

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

[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>

Bzoj3786 星系探索

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

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

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

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

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

BZOJ3786 星系探索 蒟蒻出题人给跪

本蒟蒻闲得蛋疼于是在BZOJ上加了一道水题,果然被瞬间水过... 只能说本蒟蒻实在是太弱了. Q:你为什么要写这篇博客? A:我只是为了水经验233.... 正常向的数据.题解.标程请自行传送下面的云盘... http://pan.baidu.com/s/1qWsMHM8 吐槽: 为什么本地不开O2 10s在OJ上开O2 还需要20+s啊!!!我本来不想卡常数好不好. 因为这个原因用数组实现数据结构被卡的请见谅...现在是40s应该卡不掉了. 另外如果发现自己被卡掉请重交一次.原因不解释. 为什

bzoj3786 星际探索 splay dfs序

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