树链剖分

定义: 

siz[]数组,用来保存以x为根的子树节点个数

top[]数组,用来保存当前节点的所在链的顶端节点

hson[]数组,用来保存重儿子

drep[]数组,用来保存当前节点的深度

fa[]数组,用来保存当前节点的父亲

id[]数组,用来保存树中每个节点剖分后的新编号

两遍dfs求出以上信息,连边成重链,按照新的编号建树(有的题目这一步可省略);剩下全是线段树的操作

查询或修改的话,因为在第二个dfs中优先构造重链,所以修改深度更深的x到top[x],x再换为fa[x],直到两点top相等;

一般链剖100多行,我强力缩行到87行。。。。。。。。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#define rs ((o<<1)|1)
#define ls (o<<1)
#define MAXX 300010
using namespace std;
struct data{
  int nxt,to;
}edge[MAXX*2];
int segtree[MAXX*4],head[MAXX],top[MAXX],siz[MAXX],hson[MAXX],id[MAXX];
int n,f,t,m,deep[MAXX],fa[MAXX],lazy[MAXX*4],tot,sm,a,b;
void add(int fro,int too){edge[++tot].nxt=head[fro];edge[tot].to=too;head[fro]=tot;}
void dfs1(int num,int father){
  hson[num]=0;deep[num]=deep[father]+1;siz[num]=1;fa[num]=father;
  for(int i=head[num];i;i=edge[i].nxt)if(father!=edge[i].to){
      int too=edge[i].to;
      dfs1(too,num);
      siz[num]+=siz[too];
      if(siz[hson[num]]<siz[too])hson[num]=too;
  }
}
void dfs2(int num,int toop){
  id[num]=++sm;top[num]=toop;
  if(hson[num])dfs2(hson[num],toop);
  for(int i=head[num];i;i=edge[i].nxt)if(edge[i].to!=hson[num]&&edge[i].to!=fa[num])dfs2(edge[i].to,edge[i].to);
}
void down(int o,int l,int r){
  int mid=(r+l)>>1;
  segtree[ls]+=(mid-l+1)*lazy[o],segtree[rs]+=lazy[o]*(r-mid);
  lazy[rs]+=lazy[o],lazy[ls]+=lazy[o],lazy[o]=0;
}
void update(int o,int L,int R,int ll,int rr){
  if(L!=R)down(o,L,R);
  if(L>=ll&&R<=rr){segtree[o]+=(R-L+1);lazy[o]++;return;}
  int mid=(L+R)>>1;
  if(mid<ll)update(rs,mid+1,R,ll,rr);
  else if(mid>=rr)update(ls,L,mid,ll,rr);
  else update(ls,L,mid,ll,rr),update(rs,mid+1,R,ll,rr);
  segtree[o]=segtree[ls]+segtree[rs];
}
int ask(int o,int L,int R,int ll,int rr){
  if(L!=R)down(o,L,R);
  if(L>=ll&&R<=rr)return segtree[o];
   int mid=(L+R)>>1;
  if(mid<ll)return ask(rs,mid+1,R,ll,rr);
  else if(mid>=rr)return ask(ls,L,mid,ll,rr);
  else return ask(ls,L,mid,ll,rr)+ask(rs,mid+1,R,ll,rr);
}
void work1(int x,int y){
  while(top[x]!=top[y]){
    if(deep[top[x]]<deep[top[y]])swap(x,y);
    update(1,1,n,id[top[x]],id[x]);
    x=fa[top[x]];
  }
  if(deep[x]>deep[y])swap(x,y);
  update(1,1,n,id[x],id[y]);
}
int work2(int x,int y){
  int ans=0;
  while(top[x]!=top[y]){
    if(deep[top[x]]<deep[top[y]])swap(x,y);
    ans+=ask(1,1,n,id[top[x]],id[x]);
    x=fa[top[x]];
  }
  if(deep[x]>deep[y])swap(x,y);
  ans+=ask(1,1,n,id[x],id[y]);
  return ans;
}
int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  scanf("%d",&n);
  for(int i=1;i<n;++i)scanf("%d%d",&f,&t),add(f,t),add(t,f);
  dfs1(1,0),dfs2(1,1);
  scanf("%d",&m);
  for(int i=1;i<=m;++i){
    int c;
    scanf("%d%d%d",&c,&a,&b);
    if(c==1)work1(a,b);
    else printf("%d\n",work2(a,b));
  }
  return 0;
}


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值