Bzoj3786星系探索:splay维护dfs序

4 篇文章 0 订阅

题目链接:星系探索

最近越来越懒了都不想写blog了QAQ

子树修改,链查询,树结构改变,单纯的lct已经不能满足这道题了QAQ

考虑dfs序,设每个点再dfs序中的位置为st[x],ed[x],我们在st[x]的位置上赋值为x的值,在ed[x]上赋值为-x的值,这样查询链的时候不在链上的点的贡献会被抵消

修改也很简单

至于改变父节点,只要把当前子树的那一段dfs序删去并加在新父节点的后面即可

然而这个代码在BZ上迷之RE在本地机上AC是什么鬼?

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=200051;
int n,m,a[maxn],fz[maxn],w[maxn],sta[maxn];
int st[maxn],ed[maxn],root,tot=0,h[maxn],ind=0;
struct edge{int to,next;}G[maxn];
struct Nodes{
	int c[2],s0,s1,add,fa;
	LL s;
};
struct splay_tree{
	Nodes t[maxn];
	void push_up(int x){
		t[x].s0=t[t[x].c[0]].s0+t[t[x].c[1]].s0+(w[x]==1);
		t[x].s1=t[t[x].c[0]].s1+t[t[x].c[1]].s1+(w[x]==-1);
		t[x].s=t[t[x].c[0]].s+t[t[x].c[1]].s+fz[x];
	}
	void update(int x,int v){
		if (!x) return;
		t[x].s+=1LL*(t[x].s0-t[x].s1)*v;
		fz[x]+=w[x]*v;
		t[x].add+=v;
	}
	void push_down(int x){
		if (!t[x].add||!x) return;
		update(t[x].c[0],t[x].add);
		update(t[x].c[1],t[x].add);
		t[x].add=0;
	}
	void pre(int x){
		int top=0; 
		for (;x;x=t[x].fa) sta[++top]=x;
		while (top) push_down(sta[top--]);
	}
	void rotate(int p,int x){
        int mark= p==t[x].c[1];
        int y=t[p].c[mark^1],z=t[x].fa;
        if (t[z].c[0]==x) t[z].c[0]=p;
        if (t[z].c[1]==x) t[z].c[1]=p;
        if (y) t[y].fa=x; t[x].c[mark]=y;
        t[p].fa=z; t[p].c[mark^1]=x; t[x].fa=p;
        push_up(x);
    }
 	void splay(int p,int k){
		pre(p);
        while (t[p].fa!=k){
            int x=t[p].fa,y=t[x].fa;
            if (y==k) rotate(p,x);
            else if (p==t[x].c[0]^x==t[y].c[0]) rotate(p,x),rotate(p,y);
            else rotate(x,y),rotate(p,x);
        } push_up(p);
        if (!k) root=p; return;
    }
	int findmin(int x){
		while (t[x].c[0]) x=t[x].c[0];
		return x;
	}
	int findmax(int x){
		while (t[x].c[1]) x=t[x].c[1];
		return x;
	}
	void split(int x,int y){
		splay(x,0);
		int t1=findmax(t[x].c[0]);
		splay(y,0);
		int t2=findmin(t[y].c[1]);
		splay(t1,0);
		splay(t2,root);
	}
	void build(int l,int r,int fat){
		if (l>r) return;
		int mid=(l+r)>>1;
		t[mid].fa=fat;
		if (fat) t[fat].c[mid>fat]=mid;
		if(l==r){t[l].s=fz[l];t[l].s0=w[l]==1;t[l].s1=1-t[l].s0;return;}
		build(l,mid-1,mid); build(mid+1,r,mid);
		push_up(mid);
	}
	void work1(){
        int x; scanf("%d",&x);
		splay(1,0); splay(st[x],root);
		printf("%lld\n",t[t[t[root].c[1]].c[0]].s+fz[root]+fz[t[root].c[1]]);
	}
	void work2(){
        int x,y; scanf("%d%d",&x,&y);
		splay(st[x],0); splay(ed[x],root);
		update(t[t[root].c[1]].c[0],y);
		fz[root]+=w[root]*y;
		fz[t[root].c[1]]+=w[t[root].c[1]]*y;
		push_up(t[root].c[1]);
		push_up(root);
	}
	void work3(){
		int x,y; scanf("%d%d",&x,&y);
		split(st[x],ed[x]);
		int tmp=t[root].c[1],tmp2=t[tmp].c[0];
		t[tmp].c[0]=0;
		push_up(tmp); push_up(root);
		splay(st[y],0);
		splay(findmin(t[root].c[1]),root);
		t[t[root].c[1]].c[0]=tmp2;
		t[tmp2].fa=t[root].c[1];
		push_up(t[root].c[1]); push_up(root);
	}
}s;

void add(int x,int y){
	G[++tot].to=y;G[tot].next=h[x];h[x]=tot;
}

void dfs(int x){
	st[x]=++ind; fz[ind]=a[x]; w[ind]=1;
	for (int i=h[x];i;i=G[i].next) if (!st[G[i].to]) dfs(G[i].to);
	ed[x]=++ind; fz[ind]=-a[x]; w[ind]=-1;
}

int main(){
	scanf("%d",&n);
	for (int i=2;i<=n;++i){
		int x; scanf("%d",&x);
		add(x,i);
	}
	for (int i=1;i<=n;++i) scanf("%d",&a[i]);
	ind=1;
	dfs(1);
	s.build(1,ind+1,0);
	scanf("%d",&m);
	for (int i=1;i<=m;++i){
		char ch; cin>>ch;
		if (ch=='Q') s.work1();
		else if (ch=='F') s.work2();
		else if (ch=='C') s.work3();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值