Evanyou Blog 彩带

LibreOJ #139 树链剖分 [树链剖分,线段树]

  题目传送门

树链剖分

题目描述

这是一道模板题。

给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:

  • 换根:将一个指定的节点设置为树的新根。

  • 修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。

  • 修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。

  • 询问路径:询问某条路径上节点的权值和。

  • 询问子树:询问某个子树内节点的权值和。

输入格式

第一行为一个整数 n,表示节点的个数。

第二行 nn个整数表示第 i 个节点的初始权值 ai​​。

第三行 n1 个整数,表示i+1 号节点的父节点编号 $f_{i+1}\ (1 \leqslant f_{i+1} \leqslant n)$

第四行一个整数 m,表示操作个数。

接下来 m 行,每行第一个整数表示操作类型编号:$(1 \leqslant u, v \leqslant n)$

  • 若类型为 1,则接下来一个整数 u,表示新根的编号。

  • 若类型为 2,则接下来三个整数 u,v,k,分别表示路径两端的节点编号以及增加的权值。

  • 若类型为 3,则接下来两个整数 u,k,分别表示子树根节点编号以及增加的权值。

  • 若类型为 4,则接下来两个整数 u,v,表示路径两端的节点编号。

  • 若类型为 5,则接下来一个整数 u,表示子树根节点编号。

输出格式

对于每一个类型为 4 或 5 的操作,输出一行一个整数表示答案。

样例

样例输入

6
1 2 3 4 5 6
1 2 1 4 4
6
4 5 6
2 2 4 1
5 1
1 4
3 1 2
4 2 5

样例输出

15
24
19

数据范围与提示

对于 100% 的数据,$1\leqslant n,m,k,a_i\leqslant 10^5$​。数据有一定梯度。


  分析:

  虽然说是树剖模板,但是这个换根操作确实清奇。。。在集训的时候遇到了这题,当时反正是弃疗了。。。实际上换根操作并没有什么影响,在修改查询的时候判断一下就可以了。

  Code:

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+7;
int n,m,root,a[N],head[N],cnt,fa[N],dep[N];
int top[N],dfn[N],num[N],id,size[N],hson[N];
struct Node{int to,next;}edge[N<<1];
struct Seg{
  ll seg[N<<4],sign[N<<4];
  void pushup(int rt)
  {
    seg[rt]=seg[rt<<1]+seg[rt<<1|1];
  }
  void pushdown(int l,int r,int rt)
  {
    if(!sign[rt])return;
    int mid=(l+r)>>1;
    seg[rt<<1]+=sign[rt]*(mid-l+1);
    seg[rt<<1|1]+=sign[rt]*(r-mid);
    sign[rt<<1]+=sign[rt];
    sign[rt<<1|1]+=sign[rt];
    sign[rt]=0;
  }
  void build(int l,int r,int rt)
  {
    if(l>r)return;
    if(l==r){
      seg[rt]=a[dfn[l]];return;}
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
  }
  void update(int l,int r,int rt,int L,int R,ll C)
  {
    if(l>R||r<L)return;
    if(L<=l&&r<=R){
      seg[rt]+=(r-l+1)*C;sign[rt]+=C;return;}
    int mid=(l+r)>>1;
    pushdown(l,r,rt);
    if(L<=mid)update(l,mid,rt<<1,L,R,C);
    if(R>mid)update(mid+1,r,rt<<1|1,L,R,C);
    pushup(rt);
  }
  ll quary(int l,int r,int rt,int L,int R)
  {
    if(l>R||r<L)return 0;
    if(L<=l&&r<=R){return seg[rt];}
    int mid=(l+r)>>1;ll ret=0;
    pushdown(l,r,rt);
    if(L<=mid)ret+=quary(l,mid,rt<<1,L,R);
    if(R>mid)ret+=quary(mid+1,r,rt<<1|1,L,R);
    return ret;
  }
}T;
inline int read()
{
  char ch=getchar();int num=0;bool flag=false;
  while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
  while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
  return flag?-num:num;
}
inline void add(int x,int y)
{
  edge[++cnt].to=y;
  edge[cnt].next=head[x];
  head[x]=cnt;
}
inline void dfs1(int u)
{
  size[u]=1;
  for(int i=head[u];i!=-1;i=edge[i].next){
    int v=edge[i].to;if(v==fa[u])continue;
    fa[v]=u;dep[v]=dep[u]+1;
    dfs1(v);size[u]+=size[v];
    if(size[v]>size[hson[u]])hson[u]=v;
  }
}
inline void dfs2(int u,int now)
{
  top[u]=now;num[u]=++id;dfn[id]=u;
  if(!hson[u])return;dfs2(hson[u],now);
  for(int i=head[u];i!=-1;i=edge[i].next){
    int v=edge[i].to;
    if(v==fa[u]||v==hson[u])continue;
    dfs2(v,v);}
}
inline int check(int x)
{
  if(x==root)return -1;
  if(!(num[x]<num[root]&&num[x]+size[x]-1>=num[root]))return 0;
  int now=root;
  while(dep[now]>dep[x]){if(fa[top[now]]==x)return top[now];now=fa[top[now]];}
  return hson[x];
}
inline void modify_path(int x,int y,ll z)
{
  int fax=top[x],fay=top[y];
  while(fax!=fay){
    if(dep[fax]<dep[fay]){
      swap(fax,fay);swap(x,y);}
    T.update(1,n,1,num[fax],num[x],z);
    x=fa[fax];fax=top[x];}
  if(dep[x]>dep[y])swap(x,y);
  T.update(1,n,1,num[x],num[y],z);
}
inline int modify_tree(int x,ll y)
{
  int ka=check(x);
  switch (ka){
  case -1:T.update(1,n,1,1,n,y);break;
  case 0:T.update(1,n,1,num[x],num[x]+size[x]-1,y);break;
  default:T.update(1,n,1,1,n,y);T.update(1,n,1,num[ka],num[ka]+size[ka]-1,-y);break;
  }
}
inline ll quary_path(int x,int y)
{
  int fax=top[x],fay=top[y];ll ret=0;
  while(fax!=fay){
    if(dep[fax]<dep[fay]){
      swap(fax,fay);swap(x,y);}
    ret+=T.quary(1,n,1,num[fax],num[x]);
    x=fa[fax];fax=top[x];}
  if(dep[x]>dep[y])swap(x,y);
  ret+=T.quary(1,n,1,num[x],num[y]);
  return ret;
}
inline ll quary_tree(int x)
{
  int ka=check(x);ll ret;
  switch (ka){
  case -1:ret=T.seg[1];break;
  case 0:ret=T.quary(1,n,1,num[x],num[x]+size[x]-1);break;
  default:ret=T.seg[1]-T.quary(1,n,1,num[ka],num[ka]+size[ka]-1);break;
  }
  return ret;
}
int main()
{
  n=read();int x,y,z,opt;root=1;
  memset(head,-1,sizeof(head));
  for(int i=1;i<=n;i++)a[i]=read();
  for(int i=2;i<=n;i++){
    x=read();add(x,i);add(i,x);}
  dep[1]=1;dfs1(1);dfs2(1,1);
  T.build(1,n,1);m=read();
  for(int i=1;i<=m;i++){
    opt=read();
    if(opt==1){x=read();root=x;}
    else if(opt==2){
      x=read();y=read();z=read();
      modify_path(x,y,z);}
    else if(opt==3){
      x=read();y=read();
      modify_tree(x,y);}
    else if(opt==4){
      x=read();y=read();
      printf("%lld\n",quary_path(x,y));}
    else {x=read();
      printf("%lld\n",quary_tree(x));}}
  return 0;
}

 

 

 

 

posted @ 2018-06-25 17:29  HolseLee  阅读(351)  评论(0编辑  收藏  举报