BZOJ3786: 星系探索 Splay+DFS序
题目大意:给你一个树,支持三种操作,子树加,点到根的路径和,改变某一个点的父亲。
分析:
看起来像一个大LCT,但是很显然,LCT做子树加我不太会啊...
那么,考虑更换一个点的父亲这个操作很有意思,也就是说明,整个树的结构不会有什么大的变化,只是某个节点的父亲变了,那么也就是相当于在DFS序上顺序的变化,那么我们就可以考虑化简它的树结构,从而在序列上解决。
而对于这道题,DFS序能解决,但需要维护更多信息,而且乘法的次数多了很多次,没试,但是应该过不去。我们可以换一种序列,也就是入栈出栈序,这样就可以完美解决了。
我们动态的维护四个信息,Splay Tree该节点的子树大小,该节点的子树和,和子树正号数量-符号数量符号,Lazy标记。之后每次PushDown的时候用Lazy*num来更新sum。其他的照常。
关键是每次查询的时候,因为我们不论怎么改变这个序列,有一点从来没有变,就是对应的树上节点在Splay Tree上的节点标号,那么每次只需要找到前驱后继即可,而找到前驱后继显然最好还是用Query_rank和Query_x分别表示找到对应节点的排名和对应排名的节点编号,非递归,记得在Query_x的时候PushDown就可以了!
之后Rank1什么的,就不用在意了...毕竟Splay的常数...大家知道就好...
不过我已经尽力了...最快优化到33s,不知道还有没有其他优化...
附上代码:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <cstdlib> #include <queue> #include <iostream> using namespace std; #define N 200005 #define ll long long #define ls ch[rt][0] #define rs ch[rt][1] #define get(rt) (ch[f[rt]][0]!=rt) #define PushUp(rt) num[rt]=num[ch[rt][0]]+num[ch[rt][1]]+flag[rt],sum[rt]=sum[ch[rt][0]]+sum[ch[rt][1]]+val[rt]*flag[rt],siz[rt]=siz[ch[rt][0]]+siz[ch[rt][1]]+1 char buf[100000],*p1,*p2; #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++) __attribute__((optimize("-O3")))int rd() { register int x=0;register char ch=nc(); while(ch<'0'||ch>'9') ch=nc(); while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=nc(); return x; } struct node{int to,next;}e[N]; int head[N],f[N],num[N],siz[N],cnt,flag[N],n,rot,ch[N][2],tmp,tims,a[N],idx[N],p[N]; ll sum[N];int add[N],val[N]; void add1(int x,int y){e[cnt]=(node){y,head[x]},head[x]=cnt++;} void dfs(int x) { idx[++tims]=x;p[x]=tims; for(int i=head[x];i!=-1;i=e[i].next)dfs(e[i].to); idx[++tims]=x+n;p[x+n]=tims; } void PushDown(int rt) { if(add[rt]) { sum[ls]+=(long long)(add[rt])*num[ls],val[ls]+=add[rt],add[ls]+=add[rt]; sum[rs]+=(long long)(add[rt])*num[rs],val[rs]+=add[rt],add[rs]+=add[rt]; add[rt]=0; } } void rotate(int rt) { int x=f[rt],y=f[x],k=get(rt); if(x!=rot)ch[y][ch[y][0]!=x]=rt;else rot=rt; ch[x][k]=ch[rt][!k];f[ch[x][k]]=x; ch[rt][!k]=x;f[x]=rt;f[rt]=y; PushUp(x);PushUp(rt); } void Splay(int rt,int y) { register int fa; for(;(fa=f[rt])!=y;rotate(rt)) if(f[fa]!=y) rotate((get(fa)==get(rt))?fa:rt); } void build(int fa,int l,int r) { if(l>r)return;int m=(l+r)>>1; ch[fa][m>fa]=m;f[m]=fa;siz[m]=1;//printf("%d %d %d\n",idx[m],m,val[m]); build(m,l,m-1);build(m,m+1,r);PushUp(m); } int query_k(int rt) { int ret=0; while(rt) { // PushDown(rt); // printf("%d %d %d\n",x,idx[rt],ret); if(rt==rot)ret+=siz[ls]+1; else if(get(rt))ret+=siz[ls]+1; else ret-=siz[rs]+1; rt=f[rt]; } return ret; } int find(int x) { register int rt=rot; while(1) { PushDown(rt); if(x<=siz[ls])rt=ls; else { x-=siz[ls]+1; if(!x)return rt; rt=rs; } } } void cut(int x) { int rt=find(query_k(p[x+n])+1);x=find(query_k(p[x])-1); Splay(x,0);Splay(rt,rot);tmp=ls; ls=f[ls]=0;PushUp(rt);PushUp(x); } void link(int x) { int rt=find(query_k(p[x])+1);x=find(query_k(p[x])); Splay(x,0);Splay(rt,rot); // printf("%d %d\n",x,rt); ls=tmp;f[tmp]=rt;PushUp(rt);PushUp(x); } void Update(int x,int c) { int rt=find(query_k(p[x+n])+1);x=find(query_k(p[x])-1); Splay(x,0);Splay(rt,rot); add[ls]+=c;val[ls]+=c; } int query(int x) { int rt=find(query_k(p[x])+1);x=1; Splay(x,0);Splay(rt,rot); printf("%lld\n",sum[ls]); }char s[2]; int main() { n=rd();memset(head,-1,sizeof(head)); for(int i=2,x;i<=n;i++)add1(rd(),i); tims=1;dfs(1);idx[++tims]=tims;idx[1]=0; for(int i=1;i<=n;i++)a[i]=rd(); for(int i=2;i<=n*2+1;i++) { int x=idx[i]; if(x<=n)val[i]=a[x],flag[i]=1; else val[i]=a[x-n],flag[i]=-1; } build(0,1,2*n+2);rot=n+1;int Q=rd(); while(Q--) { char op=nc(); while(op!='C'&&op!='Q'&&op!='F')op=nc(); int x=rd(); if(op=='Q')query(x); else if(op=='C') { cut(x); link(rd()); }else Update(x,rd()); // print(); } return 0; }