BZOJ 3786:星系探索

Portal:http://www.lydsy.com/JudgeOnline/problem.php?id=3786

解析:

我们先跑出树的括号序,对于位置\(L_i\)结点值设为\(A_i\),位置\(R_i\)结点值为\(-A_i\),则一个结点到父节点的值即为\(\sum_1^{L_i} s_i\),现在问题就变成了如何维护这个序列和如何快速求和。

我们用一棵splay来维护整个序列,需要维护splay结点的子树和,子树正负数个数,下放标记,然后改父节点直接移动一下区间,加值直接区间赋值,求和就分离出目标区间求和即可。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>

#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
#define sz(x) (int)(x.size())

using namespace std;

typedef long long LL;

const int maxn=300000+23;

struct Node{
 Node *ch[2],*f;
 int v,size;
 LL s,sum,tag,ab; //I'm going to be mad!!
 Node(int val,Node *null) {v=val,s=0,sum=0,ab=0,size=1,tag=0,ch[0]=ch[1]=f=null;}

 void maintain()
 {
  size=ch[0]->size+ch[1]->size+1;
  sum=ch[0]->sum+ch[1]->sum+s;
  ab=ch[0]->ab+ch[1]->ab+((v<0)?(-1):(1));
 }
 void pushdown()
 {
  if (tag) ch[0]->update(tag),ch[1]->update(tag),tag=0;
 }
 void update(LL val)
 {
  s+=((v<0)?(-1):(1))*val,sum+=ab*val;
  if (size>1) tag+=val;
 }
 int cmp(int k)
 {
  if ((ch[0]->size+1)==k) return -1;
  return (ch[0]->size>=k)?(0):(1);
 }
} *root,*null,*x,*y,*z,*w,*L[maxn],*R[maxn];

Node *newnode(int v) {return new Node(v,null);}

//------------------------------------------

int n,m,k,v[maxn],vs,l,r,ind,last;

LL a[maxn],val;

vector<int> G[maxn];

char c[10];

//-----------------------------

Node *rotate(Node* &o,int d) 
{
 Node* k=o->ch[d^1];
 o->ch[d^1]=k->ch[d],o->ch[d^1]->f=o;
 k->ch[d]=o,o->f=k;
 o->maintain(),k->maintain();o=k;
}

void splay(Node* &o,int k)
{
 o->pushdown();int d=o->cmp(k);
 if (d==1) k-=o->ch[0]->size+1;

 if (d!=-1)
 {
  Node* p=o->ch[d];p->pushdown();
  int d2=p->cmp(k),k2=(d2==0)?(k):(k-p->ch[0]->size-1);
  if (d2!=-1)
  {
   splay(p->ch[d2],k2);
   if (d==d2) rotate(o,d^1); else rotate(o->ch[d],d);
  }
  rotate(o,d^1);
 }
}

Node* merge(Node *x,Node *y)
{
 if (x==null) return y;
 if (y==null) return x;

 splay(x,x->size);
 x->ch[1]=y,y->f=x,x->maintain();
 return x;
}

void split(Node *o,int k,Node* &l,Node* &r)
{
 if (k<=0) {l=null,r=o;return;}
 splay(o,k);
 l=o,r=o->ch[1],l->f=r->f=null,o->ch[1]=null,l->maintain();
}

void rnk(Node *o,int &ans,int d)
{
 if (o==null) return;
 if (d==1) ans+=o->ch[0]->size+1;
 rnk(o->f,ans,(o->f->ch[0]==o)?(0):(1));
}

//-----------------------------

Node* Build(int l,int r,Node* f)
{
 if (l>r) return null;
 int mid=(l+r)>>1;
 Node *o=newnode(v[mid]);
 o->f=f,o->ch[0]=Build(l,mid-1,o),o->ch[1]=Build(mid+1,r,o); 
 if (v[mid]>0) L[v[mid]]=o,o->s=a[v[mid]]; else R[-v[mid]]=o,o->s=-a[-v[mid]];
 o->maintain();
 return o;
}

void trans(int k)
{
 v[++vs]=k;
 rep(i,0,sz(G[k])-1) trans(G[k][i]);
 v[++vs]=-k;
}

int main()
{
 scanf("%d",&n);
 rep(i,2,n)
 {
  scanf("%d",&k);
  G[k].push_back(i);
 }
 rep(i,1,n) scanf("%d",&a[i]);

 trans(1);
 
 null=newnode(0),null->size=0,null->s=null->sum=null->ab=0,root=null;
 root=Build(1,vs,null);

 scanf("%d",&m);
 rep(i,1,m)
 {
  scanf("%s",c+1);
  if (c[1]=='Q')
  {
   scanf("%d",&k);
   last=0,rnk(L[k],last,1);
   split(root,last,x,y);
   printf("%lld\n",x->sum);
   root=merge(x,y);
  }

  if (c[1]=='F')
  {
   scanf("%d%lld",&ind,&val);
   l=0,rnk(L[ind],l,1),r=0,rnk(R[ind],r,1);

   split(root,l-1,x,y);
   split(y,r-l+1,z,w);

   z->update(val);
   root=merge(x,merge(z,w));
  }

  if (c[1]=='C')
  {
   scanf("%d%d",&ind,&k);
   l=0,rnk(L[ind],l,1),r=0,rnk(R[ind],r,1);
   split(root,l-1,x,y);
   split(y,r-l+1,z,w);
   root=merge(x,w);
   
   last=0,rnk(R[k],last,1);
   split(root,last-1,x,y);
   root=merge(x,merge(z,y));
  }
 }

 return 0;
}

posted @ 2017-03-01 19:31  Krew  阅读(102)  评论(0编辑  收藏  举报