bzoj 3786: 星系探索 splay

        bzoj真是坑爹,本地跑的差不多的程序,指针比数组快到不知道哪里去了。

       然后我就开始了艰辛的卡常数,最终比很多指针快多辣~~~

       由于有子树移动,那么考虑维护括号序列。也就是dfs入栈为'(',出栈为')',然后'('为正的权值。那么子树移动相当于平衡树的分裂和合并。用splay实现。

       PS:求卡常数大神优化代码>.<

AC代码如下(目前最快的):

#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;

int n,tot,tp,rt,a[N],fst[N],pnt[N],nxt[N],fa[N],c[N][2],q[N];
struct node{ int sz,sc; ll icr,num,sum; }P[N];
int read(){
	int x=0; char cr=getchar();
	while (cr<'0' || cr>'9') cr=getchar();
	while (cr>='0' && cr<='9'){ x=x*10+cr-'0'; cr=getchar(); }
	return x;
}
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=tot;
}
void maintain(int x){
	node &o=P[x],&l=P[c[x][0]],&r=P[c[x][1]];
	o.sz=l.sz+r.sz+1; o.sc=l.sc+r.sc+((x<=n)?1:-1);
	o.sum=l.sum+r.sum+o.num;
}
void mdy(int x,ll y){
	if (x){
		node &o=P[x];
		o.icr+=y; o.num+=(x<=n)?y:-y; o.sum+=o.sc*y;
	}
}
void pushdn(int x){
	ll &t=P[x].icr;
	if (t){
		mdy(c[x][0],t); mdy(c[x][1],t); t=0;
	}
}
void clr(int x){
	int i; q[tp=1]=x;
	for (i=x; i!=rt; i=fa[i]) q[++tp]=fa[i];
	while (tp) pushdn(q[tp--]);
}
int getby(int x,int l){
	int r=l^1;
	if (c[x][l]){
		for (x=c[x][l]; c[x][r]; x=c[x][r]); return x;
	} else{
		int y;
		for (y=fa[x]; y && c[y][l]==x; x=y,y=fa[y]); return y;
	}
}
void rot(int x,int &rt){
	int y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1;
	if (y!=rt) c[z][c[z][1]==y]=x; else rt=x;
	fa[x]=z; fa[y]=x; fa[c[x][r]]=y;
	c[y][l]=c[x][r]; c[x][r]=y;
	maintain(y); 
} 
void splay(int x,int &rt){
	int i,y,z; clr(x);
	for (; x!=rt; rot(x,rt)){
		y=fa[x]; z=fa[y];
		if (y!=rt) rot((c[z][0]==y ^ c[y][0]==x)?x:y,rt);
	}
	maintain(x);
}
void dfs(int x,int last){
	int i,y; q[++tp]=x;
	for (i=fst[x]; i; i=nxt[i]){
		y=pnt[i];
		if (y!=last) dfs(y,x);
	}
	q[++tp]=x+n;
}
void build(int &rt,int l,int r,int last){
	if (l>r){ rt=0; return; }
	int mid=l+r>>1;
	rt=q[mid]; fa[rt]=last;
	build(c[rt][0],l,mid-1,rt); build(c[rt][1],mid+1,r,rt);
	P[rt].num=(rt<=n)?a[rt]:-a[rt-n];
	maintain(rt);
}
int main(){
	n=read();
	int i,x,y,l,r;
	for (i=2; i<=n; i++){
		x=read(); add(x,i); add(i,x);
	}
	for (i=1; i<=n; i++) a[i]=read();
	q[tp=1]=n<<1|1;
	dfs(1,0);
	q[++tp]=n+1<<1;
	build(rt,1,tp,0);
	int cas=read(); char cr; ll ans;
	while (cas--){
		cr=getchar(); while (cr<'A' || cr>'Z') cr=getchar();
		if (cr=='Q'){
			x=read(); clr(x);
			ans=P[c[x][0]].sum+P[x].num;
			for (y=fa[x]; x!=rt; x=y,y=fa[y])
				if (x==c[y][1]) ans+=P[c[y][0]].sum+P[y].num;
			printf("%lld\n",ans);
		} else if (cr=='C'){
			x=read(); y=read();
			l=getby(x,0); r=getby(x+n,1);
			splay(l,rt); splay(r,c[l][1]);
			x=c[r][0]; c[r][0]=0;
			maintain(r); maintain(l);
			splay(y,rt); r=getby(y,1); splay(r,c[y][1]);
			fa[x]=r; c[r][0]=x;
			maintain(r); maintain(y);
		} else if (cr=='F'){
			x=read();
            y=getby(x+n,1); x=getby(x,0);
            splay(x,rt); splay(y,c[x][1]);
            mdy(c[y][0],read());
		}
	}
	return 0;
}


by lych

2016.12.6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值