hdu4942(线段树的巧用)

题意:给一颗树,三种操作:1、左旋;2、右旋;3、查询以某个点为根节点的所有的子树的有趣值的乘积;x的有趣值是这样定义的:以x为根节点,其子树的权值之和。
思路分析:
一开始我就在给定的树上做左旋和右旋操作,然后更新相应节点,但是这样做会超时,因为树的高度可以很大,最长为n。那么最坏时间复杂度为O(N*N)
需要的知识点:
1、 了解左/右旋操作;左旋右旋的条件:以右旋为例,如果要p结点右旋,那么p结点一定要有左儿子结点
2、 旋转不改变中序遍历的结果。这个自己举几个例子理解下就可以了。

有了上面的基础之后那么我们就可以将树形转化成线性。每次旋转只会影响两个节点,我们只要对这两个节点做单点更新即可。具体注释见代码

#pragma comment (linker,"/STACK:102400000,102400000")
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define N 100005
#define LL __int64
#define MOD 1000000007
#define inf 0x7ffffff
#define eps 1e-9
#define pi acos(-1.0)
using namespace std;
struct node
{
    int son[2],lr[2];//son[0-1]左右儿子、lr[0-1]在线段树中的左右区间
    int w,fa;
    LL val;//有趣值
}s[N];
int real[N],idx[N];//idx[i] = x 表示第i个节点在线段树中的位置,real[i] = x表示线段树中的i节点在树中的编号  一定要理解清楚
int temp;
struct node1
{
    int l,r;
    LL mul;
}tree[N<<2];
void mid_dfs(int o)//中序遍历
{
    s[o].val = s[o].w;
    if(s[o].son[0])
    {
        mid_dfs(s[o].son[0]);
        s[o].val += s[ s[o].son[0] ].val;
        s[o].lr[0] = s[ s[o].son[0] ].lr[0];
    }
    else s[o].lr[0] = temp;
    idx[o] = temp;// 第o个数在线段树中是第temp个数
    real[temp] = o;
    temp++;
    if(s[o].son[1])
    {
        mid_dfs(s[o].son[1]);
        s[o].val += s[ s[o].son[1] ].val;
        s[o].lr[1] = s[ s[o].son[1] ].lr[1];
    }
    else s[o].lr[1] = temp-1;
    s[o].val %= MOD;
}
void pushUp(int o)
{
    tree[o].mul = tree[2*o].mul*tree[2*o+1].mul%MOD;
}
void build(int o,int l,int r)
{
    tree[o].l = l;
    tree[o].r = r;
    if(l == r)
    {
        tree[o].mul = s[real[l]].val;
        return;
    }
    int m = (l+r)/2;
    build(2*o,l,m);
    build(2*o+1,m+1,r);
    pushUp(o);
}
void update(int o,int pos,LL v)
{
    if(tree[o].l == tree[o].r)
    {
        tree[o].mul = v;
        return;
    }
    int m = (tree[o].l + tree[o].r)/2;
    if(pos <= m) update(2*o,pos,v);
    else update(2*o+1,pos,v);
    pushUp(o);
}
LL query(int o,int x,int y)
{
    if(x <= tree[o].l && tree[o].r <= y)
        return tree[o].mul%MOD;
    LL ans = 1;
    int m = (tree[o].l+tree[o].r)/2;
    if(x <= m) ans = ans*query(2*o,x,y)%MOD;
    if(y > m) ans = ans*query(2*o+1,x,y)%MOD;
    return ans%MOD;
}
void rotation(int op,int o)//op = 0,代表右旋;这里有很多细节,每次旋转都要维护树的信息,包括其儿子/父亲节点,在线段树中对应的左右区间,有趣值
{
    int ss = s[o].son[op];
    if(ss == 0) return;
    int t = s[ss].son[1^op];
    int fa = s[o].fa;
    if(s[fa].son[1] == o) s[fa].son[1] = ss;
    else s[fa].son[0] = ss;
    s[ss].fa = fa;

    s[o].fa = ss;
    s[ss].son[1^op] = o;

    if(t)
    {
         s[t].fa = o;
         s[o].son[op] = t;
         s[o].lr[op] = s[t].lr[op];
    }
    else
    {
        s[o].son[op] = 0;
        s[o].lr[op] = idx[o];
    }
    s[ss].lr[1^op] = s[o].lr[1^op];

    s[ss].val = s[o].val;
    s[o].val = s[o].w;
    if(s[o].son[0]) s[o].val += s[ s[o].son[0] ].val;
    if(s[o].son[1]) s[o].val += s[ s[o].son[1] ].val;
    s[o].val %= MOD;

    update(1,idx[o],s[o].val);
    update(1,idx[ss],s[ss].val);
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
    int t,ca = 1;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        int i;
        s[1].fa = 0;
        for(i = 1; i <= n; i++)
        {
            scanf("%d%d%d",&s[i].w,&s[i].son[0],&s[i].son[1]);
            s[ s[i].son[0] ].fa = s[ s[i].son[1] ].fa = i;
        }
        temp = 1;
        mid_dfs(1);
        build(1,1,n);
        printf("Case #%d:\n",ca++);
        while(m--)
        {
            int p,v;
            scanf("%d%d",&p,&v);
            if(p == 2)
                printf("%I64d\n",query(1,s[v].lr[0],s[v].lr[1]) );
            else rotation(p,v);
        }

    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值