BZOJ 3786 星系探索

DFS序+平衡树

刚开始在想平衡树维护树剖,发现这样并不能资瓷删点。而且好像复杂度还很劣。

然后LCT目测搞不了子树修改。

仔细观察, 一个点的答案只能贡献给所有子树里的询问,无视子树外的询问。也就是子树内和子树外是两回事,那就对一个点维护进入它的DFS序位置和从它子树出去的DFS序位置,一正一负,子树内的询问贡献一个正的,子树外的询问上负的直接抵消,这样就可以直接询问前缀和了。

#include<cstdio>
#include<iostream>
#define N 300005
#define key root->ch[1]->ch[0]
#define pr(_i) cout<<#_i<<" = "<<_i<<endl
using namespace std;
namespace runzhe2000
{
    typedef long long ll;
    char opt[3];
    int n, m, ecnt, last[N], w[N];
    struct edge{int next, to;}e[N];
    void addedge(int a, int b)
    {
        e[++ecnt] = (edge){last[a], b};
        last[a] = ecnt;
    }

    struct node
    {
        node *fa, *ch[2];
        ll val, sum, tag;
        int base, totbase;
    }mem[N], *tot, *null, *root, *pos[N][2];
    node* newnode(){node *p = ++tot; *p = *null; return p;}
    void init()
    {
        null = tot = mem;
        null->fa = null->ch[0] = null->ch[1] = null;
        null->val = null->sum = null->base = 0;
        root = newnode(); root->ch[1] = newnode();
        root->ch[1]->fa = root;
    }
    int type(node *p){return p->fa->ch[1] == p ? 1 : 0;}
    void pushdown(node *p)
    {
        if(!p->tag) return;
        for(int i = 0; i <= 1; i++) if(p->ch[i] != null)
        {
            p->ch[i]->val += p->tag;
            p->ch[i]->sum += p->tag * p->ch[i]->totbase;
            p->ch[i]->tag += p->tag;
        }
        p->tag = 0;
    }
    void pushup(node *p)
    {
        pushdown(p);
        p->sum = p->val*p->base + p->ch[0]->sum + p->ch[1]->sum;
        p->totbase = p->base + p->ch[0]->totbase + p->ch[1]->totbase;
    }
    void update(node *p)
    {
        if(p->fa != null)update(p->fa);
        pushdown(p);
    }
    void rotate(node *p)
    {
        node *f = p->fa; int d = type(p);
        (p->fa = f->fa) != null ? p->fa->ch[type(f)] = p : 0;
        (f->ch[d] = p->ch[!d]) != null ? f->ch[d]->fa = f : 0;
        p->ch[!d] = f, f->fa = p; pushup(f);
    }
    void splay(node *p, node *to = null)
    {
        update(p);
        for(; p->fa != to; )
        {
            if(p->fa->fa == to) rotate(p);
            else if(type(p->fa) == type(p)) rotate(p->fa), rotate(p);
            else rotate(p), rotate(p);
        }
        pushup(p);
        if(to == null) root = p;
    }

    void dfs(int x)
    {
        node *p = key = pos[x][1] = newnode();
        p->val = p->sum = w[x]; p->fa = root->ch[1]; p->base = 1;
        splay(p);
        for(int i = last[x]; i; i = e[i].next)  dfs(e[i].to);
        p = key = pos[x][0] = newnode();
        p->val = w[x]; p->sum = -w[x]; p->fa = root->ch[1]; p->base = -1;
        splay(p);
    }

    node* get_l(node *p){return p->ch[0] != null ? get_l(p->ch[0]) : p;}
    node* get_r(node *p){return p->ch[1] != null ? get_r(p->ch[1]) : p;}

    void showtree(node *p)
    {
        if(p == null) return;
        printf("p = %ld ch0 = %ld ch1 = %ld fa = %ld\n",p-mem,p->ch[0]-mem,p->ch[1]-mem,p->fa-mem);
        showtree(p->ch[0]); showtree(p->ch[1]);
    }

    void fetch(node *l, node *r)
    {
        splay(l); node *ll = get_r(root->ch[0]);
        splay(r); node *rr = get_l(root->ch[1]);
        splay(ll); splay(rr,ll);
    }

    void main()
    {
        scanf("%d",&n);
        for(int i = 2, f; i <= n; i++)
        {
            scanf("%d",&f);
            addedge(f, i);
        }
        for(int i = 1; i <= n; i++) scanf("%d",&w[i]);
        init(); dfs(1); scanf("%d",&m);
        for(int i = 1; i <= m; i++)
        {
            scanf("%s",opt);
            if(opt[0] == 'Q')
            {
                int d; scanf("%d",&d);
                fetch(pos[1][1], pos[d][1]);
                printf("%lld\n",key->sum);
            }
            else if(opt[0] == 'C')
            {
                int x, y; scanf("%d%d",&x,&y);
                fetch(pos[x][1], pos[x][0]);
                node *p = key; key = null;
                pushup(root->ch[1]); pushup(root);
                splay(pos[y][1]); node *tmp = get_l(root->ch[1]);
                splay(tmp, root); key = p; p->fa = root->ch[1];
                splay(key);
            }
            else
            {
                int p, q; scanf("%d%d",&p,&q);
                fetch(pos[p][1], pos[p][0]);
                key->tag += q; key->val += q;
                splay(key);
            }
        }
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值