bzoj 3786: 星系探索(splay+dfs序)

3786: 星系探索

Time Limit: 40 Sec   Memory Limit: 256 MB
Submit: 656   Solved: 214
[ Submit][ Status][ Discuss]

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.保证操作合法。


Source

[ Submit][ Status][ Discuss]

题解:splay+dfs序

这道题卡常数,呜呜呜。。。。刚开始TLE,后来看网上说这道题卡数组,不开心,把所有的数组改成一个结构体就A掉了。

不过除去卡常不谈,这还是一道不错的题目。

我们先将树建出来,求出dfs序,但是求时候与普通的有点区别,正常的dfs序每个节点在序列中只有一个代表节点,然后用两个数组记录他子树对应区间的头尾。我们在求得时候维护一个入栈点和一个出栈点,那么入栈点到出栈点之间就包含了他子树中所有节点。

我们将入栈点的权赋值为当前点的点权,把出栈点的权赋值为当前点点权的相反数。因为求答案的时候是求该点到根路径上的点权和,只需要将[l[1],l[x]]这段区间提取到根的右儿子的左子树,这段区间的总值就是答案。因为中间那些多余的点他们出入栈的点权会相互抵消,最后只剩下路径上的点未被抵消。

如果是更改依赖关系,就将那段子树的区间提取出来,直接插入到要依赖的点的出栈点之前即可。

注意因为区间的值有正有负,所以我们还要维护区间中入栈点的个数和出栈点的个数以更新区间和的答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200013
#define LL long long 
using namespace std;
int n,m,point[N],next1[N],v[N],l[N],r[N],pos[N],val[N],tot,cnt,sz;
int num[N],pd[N],root;
struct data
{
	int fa,size,mark,ch[3];
	LL delta,sum,key,a,b;
}a[N];
void add(int x,int y)
{
	tot++; next1[tot]=point[x]; point[x]=tot; v[tot]=y;
}
void dfs(int x,int f)
{
	l[x]=++sz;
	for (int i=point[x];i;i=next1[i])
	 if (v[i]!=f)
	  	dfs(v[i],x);
	r[x]=++sz;
}
int build (int l,int r,int f)  
{  
    if (l>r) return 0;  
    int mid=(l+r)>>1;  
    int now=++cnt; num[mid]=now;  
    a[now].mark=pd[mid];
    a[now].ch[0]=build(l,mid-1,now);  
    a[now].ch[1]=build(mid+1,r,now);  
    a[now].fa=f; a[now].delta=0;  
    a[now].sum=a[now].key=(LL)pos[mid];a[now].size=a[a[now].ch[1]].size+a[a[now].ch[0]].size+1;
    a[now].sum+=a[a[now].ch[1]].sum+a[a[now].ch[0]].sum;
    a[now].a=a[a[now].ch[1]].a+a[a[now].ch[0]].a; a[now].b=a[a[now].ch[1]].b+a[a[now].ch[0]].b;
	if (a[now].mark>0)  a[now].a+=1;
	  else if (a[now].mark<0)  a[now].b+=1;  
//	cout<<l<<" "<<r<<" "<<sum[now]<<" "<<size[now]<<" "<<a[now]<<" "<<b[now]<<endl;
    return now;  
}  
int get(int x)
{
	return a[a[x].fa].ch[1]==x;
}
void update(int now)
{
	a[now].sum=a[now].key;
	a[now].sum+=a[a[now].ch[1]].sum+a[a[now].ch[0]].sum;
	a[now].a=a[a[now].ch[1]].a+a[a[now].ch[0]].a;
	a[now].b=a[a[now].ch[1]].b+a[a[now].ch[0]].b;
	if (a[now].mark>0)  a[now].a++;
	  else if (a[now].mark<0)  a[now].b++;
	a[now].size=a[a[now].ch[1]].size+a[a[now].ch[0]].size+1;
}
void pushdown(int now)
{
	if (a[now].delta) 
	{
		if (a[a[now].ch[0]].mark>0)  a[a[now].ch[0]].key+=a[now].delta;
		  else if (a[a[now].ch[0]].mark<0)  a[a[now].ch[0]].key-=a[now].delta;
		 if (a[a[now].ch[1]].mark>0)  a[a[now].ch[1]].key+=a[now].delta;
		  else if (a[a[now].ch[1]].mark<0) a[a[now].ch[1]].key-=a[now].delta;
		a[a[now].ch[0]].sum+=a[a[now].ch[0]].a*a[now].delta-a[a[now].ch[0]].b*a[now].delta;
		a[a[now].ch[1]].sum+=a[a[now].ch[1]].a*a[now].delta-a[a[now].ch[1]].b*a[now].delta;
		a[a[now].ch[0]].delta+=a[now].delta;
		a[a[now].ch[1]].delta+=a[now].delta;
		a[now].delta=0;
	}
} 
void rotate(int x)
{
	int y=a[x].fa; int z=a[y].fa; 
	pushdown(y); pushdown(x); int which=get(x);
	a[y].ch[which]=a[x].ch[which^1]; a[a[x].ch[which^1]].fa=y;
	a[y].fa=x; a[x].ch[which^1]=y; 
	if (z)  a[z].ch[a[z].ch[1]==y]=x;
	a[x].fa=z; 
	update(y); update(x);
}
void splay(int x,int tar)
{
	for (int f;(f=a[x].fa)!=tar;rotate(x))
	 if (a[f].fa!=tar)
	  rotate(get(x)==get(f)?f:x);
	if (!tar)  root=x;
}
/*int next()
{
	int now=ch[root][1];
	while (ch[now][0])  now=ch[now][0];
	return now;
}
int pre()
{
	int now=ch[root][0];
	while (ch[now][1]) now=ch[now][1];
	return now;
}*/
int next(int now)
{
	if (a[now].ch[1])
	 {
	 	now=a[now].ch[1];
	 	while (a[now].ch[0]) now=a[now].ch[0];
	 	return now;
	 }
	while (a[now].fa)
	 {
	 	int which=get(now);
	 	if (which==0)  return a[now].fa;
	 	now=a[now].fa;
	 }
}
int pre(int now)
{
	if (a[now].ch[0])  
	{
		now=a[now].ch[0];
		while (a[now].ch[1]) now=a[now].ch[1];
		return now;
	}
	while(a[now].fa)
	{
		int which=get(now);
		if (which==1) return a[now].fa;
		now=a[now].fa;
	}
}
int main()
{
	freopen("galaxy.in","r",stdin);
	freopen("galaxy.out","w",stdout);
	scanf("%d",&n);
	for (int i=2;i<=n;i++)
	 {
	 	int x; scanf("%d",&x);
	 	add(x,i);
	 }
	sz=1;
	dfs(1,0);
//	for (int i=1;i<=n;i++)
	 //cout<<l[i]<<" "<<r[i]<<endl;
	for (int i=1;i<=n;i++) scanf("%d",&val[i]);
	for (int i=1;i<=n;i++) 
	 pos[l[i]]=val[i],pos[r[i]]=-val[i],pd[l[i]]=1,pd[r[i]]=-1;
	sz++;
	build(1,sz,0);
	//for (int i=1;i<=sz;i++)  cout<<num[i]<<" ";
   // cout<<endl;
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	 {
	 	char s[10]; scanf("%s",s);
	 	int x,y;
	 	if (s[0]=='Q')
	 	 {
	 	    scanf("%d",&x);
	 	    int rr=next(num[l[x]]); int ll=pre(num[l[1]]);
			splay(ll,0); splay(rr,root);
	 	    printf("%I64d\n",a[a[a[root].ch[1]].ch[0]].sum);
		 }
		else
		if (s[0]=='C')
		 {
		 	scanf("%d%d",&x,&y);
		 	int rr=next(num[r[x]]); int ll=pre(num[l[x]]);
		 	splay(ll,0); splay(rr,root);
		 	int t=a[a[root].ch[1]].ch[0]; a[t].fa=0;
		    a[a[root].ch[1]].ch[0]=0; update(a[root].ch[1]); update(root);
		    ll=pre(num[r[y]]);
		    splay(ll,0); splay(num[r[y]],root);
		    a[a[root].ch[1]].ch[0]=t; a[t].fa=a[root].ch[1];
		    update(a[root].ch[1]); update(root);
		 }
		else
		 {
		 	LL y;
		 	scanf("%d%I64d",&x,&y);
		    int rr=next(num[r[x]]);
		    int ll=pre(num[l[x]]);
		 	splay(ll,0); splay(rr,root);
		 	int t=a[a[root].ch[1]].ch[0];
		 	a[t].delta+=y;
			if (a[t].mark>0)  a[t].key+=y;
		 	  else if (a[t].mark<0)  a[t].key-=y;
		 	a[t].sum+=a[t].a*y-a[t].b*y;
		 	update(a[root].ch[1]); update(root);
		 }
	 }
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值