[HDOJ 4942] Game on S♂play [线段树]

69 篇文章 0 订阅
27 篇文章 0 订阅

给定一棵有根树,每个点上有一个固定不变的权值,每个点的sum值定义为以它为根的子树上的点的权值的和。现有3个操作,分别是对某个点左旋,对某个点右旋,以及查询某个点为根的子树的sum值的积。

因为左旋右旋操作对树的中序遍历不进行变动,所以我们把这棵树的中序遍历写成一个序列,那么以某个点为根的子树即为这个序列的一个区间。

左旋和右旋只会造成两个点的sum值改变,只会造成两个点影响的区间的改变。所以记录每个点的sum值,他以他为根的子树的在序列中的区间两端即可。

只需要点修改sum值和区间查询sum值的积,树状数组和线段树均可,不过树状数组要求逆元...

#pragma comment (linker,"/STACK:102400000,102400000")
#include <cstdio>

const int mod=1000000007;

struct Node {
	int ls,rs,f,w,l,r,sum,dfn;
};
struct SeqNode {
	SeqNode *ls,*rs;
	int pro;
};
SeqNode b[200000];
SeqNode *bp,*seqRoot;
Node a[100001];
int realNode[100001];
int n,m,root,dfn;

void update(int i) {
	a[i].sum=a[i].w;
	if (a[i].ls) {
		a[i].l=a[a[i].ls].l;
		a[i].sum=(a[i].sum+a[a[i].ls].sum)%mod;
	} else a[i].l=a[i].dfn;
	if (a[i].rs) {
		a[i].r=a[a[i].rs].r;
		a[i].sum=(a[i].sum+a[a[i].rs].sum)%mod;
	} else a[i].r=a[i].dfn;
	//if ((a[i].ls&&a[a[i].ls].r+1!=a[i].dfn)||(a[i].rs&&a[a[i].rs].l!=a[i].dfn+1)||a[i].l>a[i].r) {
		//printf("Error: Node %d, dfn %d, ls %d, rs %d, l %d, r %d\n",i,a[i].dfn,a[i].ls,a[i].rs,a[i].l,a[i].r);
		//if (a[i].ls) printf("  LsNode: %d %d\n",a[a[i].ls].l,a[a[i].ls].r);
		//if (a[i].rs) printf("  RsNode: %d %d\n",a[a[i].rs].l,a[a[i].rs].r);
	//}
	//printf("Node %d: %d %d %d\n",i,a[i].l,a[i].r,a[i].sum);
}
void dfs(int i) {
	if (a[i].ls) dfs(a[i].ls);
	realNode[dfn]=i;
	a[i].dfn=dfn++;
	if (a[i].rs) dfs(a[i].rs);
	update(i);
}
SeqNode *makeTree(int l,int r) {
	SeqNode *ans=bp++;
	if (l==r) {
		ans->ls=ans->rs=NULL;
		ans->pro=a[realNode[l]].sum;
	} else {
		int t=(l+r)/2;
		ans->ls=makeTree(l,t);
		ans->rs=makeTree(t+1,r);
		ans->pro=((long long)ans->ls->pro*ans->rs->pro)%mod;
	}
	return ans;
}
int get(SeqNode *from,int l,int r,int ll,int rr) {
	//printf("get: %d %d %d %d\n",l,r,ll,rr);
	if (l==ll&&r==rr) return from->pro;
	int t=(l+r)/2;
	if (rr<=t) return get(from->ls,l,t,ll,rr);
	else if (t<ll) return get(from->rs,t+1,r,ll,rr);
	else return (long long)get(from->ls,l,t,ll,t)*get(from->rs,t+1,r,t+1,rr)%mod;
}
void set(SeqNode *from,int l,int r,int i,int x) {
	if (l==r) from->pro=x;
	else {
		int t=(l+r)/2;
		if (i<=t) set(from->ls,l,t,i,x);
		else set(from->rs,t+1,r,i,x);
		from->pro=(long long)from->ls->pro*from->rs->pro%mod;
	}
}

int main() {
	int t,tt,i;
	scanf("%d",&t);
	for (tt=1;tt<=t;tt++) {
		scanf("%d%d",&n,&m);
		for (i=1;i<=n;i++) {
			a[i].f=a[i].ls=a[i].rs=0;
		}
		for (i=1;i<=n;i++) {
			int w,x,y;
			scanf("%d%d%d",&w,&x,&y);
			a[i].w=w; a[i].ls=x; a[i].rs=y;
			if (x) a[x].f=i;
			if (y) a[y].f=i;
		}
		dfn=1;
		for (i=1;i<=n;i++) if (a[i].f==0) root=i;
		dfs(root);
		bp=b;
		seqRoot=makeTree(1,n);
		printf("Case #%d:\n",tt);
		for (i=0;i<m;i++) {
			int p,x;
			scanf("%d%d",&p,&x);
			if (p==0) {
				//printf("Right rotation start:\n");
				if (a[x].ls) {
					int y=a[x].ls;
					int f=a[x].f;
					a[y].f=f;
					a[x].f=y;
					a[a[y].rs].f=x;
					a[x].ls=a[y].rs;
					a[y].rs=x;
					update(x);
					update(y);
					if (f!=0) {
						if (x==a[f].ls) a[f].ls=y;
						else a[f].rs=y;
					}
					set(seqRoot,1,n,a[x].dfn,a[x].sum);
					set(seqRoot,1,n,a[y].dfn,a[y].sum);
				}
				//printf("Right rotation end:\n");
			} else if (p==1) {
				//printf("Left rotation start:\n");
				if (a[x].rs) {
					int y=a[x].rs;
					int f=a[x].f;
					a[y].f=f;
					a[x].f=y;
					a[a[y].ls].f=x;
					a[x].rs=a[y].ls;
					a[y].ls=x;
					update(x);
					update(y);
					if (f!=0) {
						if (x==a[f].ls) a[f].ls=y;
						else a[f].rs=y;
					}
					set(seqRoot,1,n,a[x].dfn,a[x].sum);
					set(seqRoot,1,n,a[y].dfn,a[y].sum);
				}
				//printf("Left rotation end:\n");
			} else {
				printf("%d\n",get(seqRoot,1,n,a[x].l,a[x].r));
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值