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