bzoj3786: 星系探索

bzoj3786: 星系探索

Description

物理学家小C的研究正遇到某个瓶颈。

他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球。主星球没有依赖星球。

我们定义依赖关系如下:若星球a的依赖星球是b,则有星球a依赖星球b.此外,依赖关系具有传递性,即若星球a依赖星球b,星球b依赖星球c,则有星球a依赖星球c.

对于这个神秘的星系中,小C初步探究了它的性质,发现星球之间的依赖关系是无环的。并且从星球a出发只能直接到达它的依赖星球b.

每个星球i都有一个能量系数wi.小C想进行若干次实验,第i次实验,他将从飞船上向星球di发射一个初始能量为0的能量收集器,能量收集器会从星球di开始前往主星球,并收集沿途每个星球的部分能量,收集能量的多少等于这个星球的能量系数。

但是星系的构成并不是一成不变的,某些时刻,星系可能由于某些复杂的原因发生变化。

有些时刻,某个星球能量激发,将使得所有依赖于它的星球以及他自己的能量系数均增加一个定值。还有可能在某些时刻,某个星球的依赖星球会发生变化,但变化后依然满足依赖关系是无环的。

现在小C已经测定了时刻0时每个星球的能量系数,以及每个星球(除了主星球之外)的依赖星球。接下来的m个时刻,每个时刻都会发生一些事件。其中小C可能会进行若干次实验,对于他的每一次实验,请你告诉他这一次实验能量收集器的最终能量是多少。

Input

第一行一个整数n,表示星系的星球数。

接下来n-1行每行一个整数,分别表示星球2-n的依赖星球编号。

接下来一行n个整数,表示每个星球在时刻0时的初始能量系数wi.

接下来一行一个整数m,表示事件的总数。

事件分为以下三种类型。

(1)"Q di"表示小C要开始一次实验,收集器的初始位置在星球di.

(2)"C xi yi"表示星球xi的依赖星球变为了星球yi.

(3)"F pi qi"表示星球pi能量激发,常数为qi.

 

 

 

Output

对于每一个事件类型为Q的事件,输出一行一个整数,表示此次实验的收集器最终能量。

 

 

Sample Input

3
1
1
4 5 7
5
Q 2
F 1 3
Q 2
C 2 3
Q 2

Sample Output

9
15
25

HINT

 

n<=100000,m<=300000,1<di,xi<=n,wi,qi<=100000.保证操作合法。注意w_i>=0

 

 

 

Source

By 佚名上传


solution

欧拉序+splay

子树修改很难实现啊,怎么办呢?

任神犇:欧拉序,第一次的记为正,第二次记为负,这样子维护前缀和就可以了。

orz!!

如神犇所说,按欧拉序为下标建splay,支持区间修改,区间交换。

当时想了很久都在想一个问题:我区间都换了,欧拉序也变了,那怎么才能把点找出来呢?

首先,明确:

tr[x]维护着欧拉序为x的答案,这是不会变的。

而树结构的改变,意味着欧拉序的改变。

也就是我们已知欧拉序,就可以取出一个点(虽然我不知道它在树的哪里)

那我沿着这个点往根跳,就可以知道有多少个点比他小,也就是它排在第几位了

然后就很方便啦。

注意:

1.欧拉序下标最好从2开始,不然很麻烦

2.因为要严格按欧拉序建树,建时直接就是tr[mid]=W[mid]..

3.注意w为0的情况

4,swap取L[y]时要推标记(kth)

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 200005 
#define ll long long
#define inf 1e9
using namespace std;
int n,m,head[maxn],L[maxn],R[maxn],w[maxn],W[maxn],tot;
int sc=1,root;
struct node{
	int v,nex;
}e[maxn];
struct no{
	int ch[2],f,tp,sz;
	ll v,bj,sum,stp;
}tr[maxn];
void lj(int t1,int t2){
	e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void dfs(int k){
	L[k]=++sc;w[sc]=(W[k]!=0)?W[k]:inf;
	for(int i=head[k];i;i=e[i].nex){
		dfs(e[i].v);
	}
	R[k]=++sc;w[sc]=(W[k]!=0)?-W[k]:-inf;
}
int get(int x){
	return tr[tr[x].f].ch[1]==x;
}
void wh(int k){
	if(!k)return;
	int ls=tr[k].ch[0],rs=tr[k].ch[1];
	tr[k].sum=tr[ls].sum+tr[rs].sum+tr[k].v;
	tr[k].stp=tr[ls].stp+tr[rs].stp+tr[k].tp;
	tr[k].sz=tr[ls].sz+tr[rs].sz+1;
}
void update(int k,ll v){
	if(!k)return;
	tr[k].bj+=v;tr[k].sum+=tr[k].stp*v;
	tr[k].v+=tr[k].tp*v;
}
void down(int k){
	if(tr[k].bj){
		update(tr[k].ch[0],tr[k].bj);
		update(tr[k].ch[1],tr[k].bj);
		tr[k].bj=0;
	}
}
int build(int fa,int l,int r){
	if(l>r)return 0;
	int mid=l+r>>1,k=mid;
	tr[k].f=fa;tr[k].v=tr[k].sum=(w[k]!=inf&&w[k]!=-inf)?w[k]:0;
	tr[k].tp=tr[k].stp=(w[k]>0?1:-1);
	tr[k].ch[0]=build(k,l,mid-1);
	tr[k].ch[1]=build(k,mid+1,r);
	wh(k);return k;
}
int find(int x){// Å·À­ÐòxÊǵڼ¸´óµÄÊý 
	int sum=tr[tr[x].ch[0]].sz;
	while(x!=root){
		if(get(x))sum+=tr[tr[tr[x].f].ch[0]].sz+1;
		x=tr[x].f;
	}
	return sum+1;
}
int Kth(int x){// µÚx´óÊÇË­ 
	int k=root;
	while(1){
		down(k);
		//cout<<k<<' '<<x<<endl;
		int ls=tr[k].ch[0];
		if(tr[ls].sz>=x)k=ls;
		if(tr[ls].sz==x-1)return k;
		if(tr[ls].sz<x-1)x-=tr[ls].sz+1,k=tr[k].ch[1];
	}
}
void rotate(int x){
	int y=tr[x].f,z=tr[y].f;
	int wx=get(x),wy=get(y);
	tr[z].ch[wy]=x;tr[x].f=z;
	tr[y].ch[wx]=tr[x].ch[wx^1];tr[tr[x].ch[wx^1]].f=y;
	tr[x].ch[wx^1]=y;tr[y].f=x;
	wh(y),wh(x);
}
void splay(int x,int g){
	while(tr[x].f!=g){
		int y=tr[x].f,z=tr[y].f;
		if(z!=g)rotate(get(x)==get(y)?y:x);
		rotate(x);
	}
	if(!g)root=x;
}

void print_tr(int k){
	if(!k)return;
	down(k);
	print_tr(tr[k].ch[0]);
	printf("k:%d v:%d sum:%d tp:%d sz:%d f:%d\n",k,tr[k].v,tr[k].sum,tr[k].tp,tr[k].sz,tr[k].f);
	print_tr(tr[k].ch[1]);
}
int main(){
	cin>>n;
	for(int i=2,t;i<=n;i++)scanf("%d",&t),lj(t,i);
  	for(int i=1;i<=n;i++)scanf("%d",&W[i]);
  	dfs(1);
  	//for(int i=1;i<=n;i++)cout<<L[i]<<' '<<R[i]<<endl; 
  	root=build(0,1,n+n+2);
  	
  	scanf("%d",&m);
  	for(int i=1,x,y;i<=m;i++){
  		char op;scanf(" %c",&op);
  		if(op=='Q'){
  			scanf("%d",&x);
			int a=Kth(find(L[x])+1);
			//cout<<"a "<<a<<' '<<x<<' '<<L[x]<<' '<<find(L[x])<<endl;
			splay(a,0);//print_tr(root);
			printf("%lld\n",tr[tr[root].ch[0]].sum);
		}
  		if(op=='C'){
  			scanf("%d%d",&x,&y);
  			int a=Kth(find(L[x])-1),b=Kth(find(R[x])+1);
  			//cout<<a<<' '<<b<<endl;
  			splay(a,0);splay(b,a);
  			//print_tr(root);
  			int t=tr[tr[root].ch[1]].ch[0];
  			tr[tr[root].ch[1]].ch[0]=0;
			wh(tr[root].ch[1]),wh(root);
  			a=Kth(find(L[y])),b=Kth(find(L[y])+1);
  			splay(a,0);splay(b,a);
  			tr[tr[root].ch[1]].ch[0]=t;tr[t].f=tr[root].ch[1];
  			wh(tr[root].ch[1]),wh(root);
		}
  		if(op=='F'){
  			scanf("%d%d",&x,&y);
  			int a=Kth(find(L[x])-1),b=Kth(find(R[x])+1);
  			//cout<<"a b:"<<a<<' '<<b<<endl;
  			
  			splay(a,0);splay(b,a);
  			int t=tr[tr[root].ch[1]].ch[0];
  			update(t,y);
			//print_tr(root);
		}
	}
	return 0;
}

 

posted @ 2019-01-26 18:26 liankewei 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值