LOJ146 dfs序3 题解(dfs序+树状数组+树上差分)

题目:LOJ146.
题目大意:给定一棵 n n n个点的树,要求支持以下操作:
1.格式 1   a   b   c 1\,a\,b\,c 1abc,表示把链 ( a , b ) (a,b) (a,b)上的点点权加上 c c c.
2.格式 2   a 2\,a 2a,表示查询点 a a a的点权.
3.格式 3   a 3\,a 3a,表示查询点 a a a的子树点权和.
设操作次数为 m m m,则 1 ≤ n , m ≤ 1 0 6 1\leq n,m\leq 10^6 1n,m106.

本来的想法是给大力跑出这棵树的dfs序,然后用 1 1 1 − 1 -1 1标号,并用线段树维护的,但是线段树常数太大貌似并过不了这道题…

于是我们考虑换用树状数组,仍然利用dfs序,只不过现在我们变成了用dfs序维护每个点到根这些链被直接会被增加多少,也可以看做是自底向上的差分,我们记点 k k k的这个值为 v [ k ] v[k] v[k].

然后我们维护链加就变成了直接给一个点加,当然由于不是一个点到根的链需要用到树上差分;查询点权变成了查询子树和;查询子树和…等等子树和怎么搞?

容易发现查询 x x x的子树和,可以统计子树中每个节点 k k k的贡献,容易发现节点 k k k的贡献为 v [ k ] ∗ ( d e p [ k ] − d e p [ x ] + 1 ) v[k]*(dep[k]-dep[x]+1) v[k](dep[k]dep[x]+1),其中 d e p [ k ] dep[k] dep[k]表示 k k k的深度.那么我们发现这个式子可以拆成 v [ k ] ∗ d e p [ k ] − v [ k ] ∗ ( d e p [ x ] − 1 ) v[k]*dep[k]-v[k]*(dep[x]-1) v[k]dep[k]v[k](dep[x]1),后半个我们已经维护了 v [ k ] v[k] v[k]之和再乘 d e p [ x ] − 1 dep[x]-1 dep[x]1即可,前半个我们可以通过多用一个树状数组维护 v [ k ] ∗ d e p [ k ] v[k]*dep[k] v[k]dep[k]来实现.

时间复杂度 O ( ( n + m ) log ⁡ n ) O((n+m)\log n) O((n+m)logn).注意这道题卡常严重,建议输入用快读且找LCA用树剖或者tarjan.

代码如下:

#include<bits/stdc++.h> 
  using namespace std;
  
#define Abigail inline void
typedef long long LL;

const int N=1000000;

int Ri(){
  int x=0,y=1;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar()) if (c=='-') y=-1;
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x*y;
}

struct side{
  int y,next;
}e[N*2+9];
int lin[N+9],top,n,m,rot;

void Ins(int x,int y){
  e[++top].y=y;
  e[top].next=lin[x];
  lin[x]=top;
}

int ld[N+9],rd[N+9],ord[N+9],co;
struct node{
  int fa,son,top,siz,dep;
}d[N+9];
LL c[2][N+9],v[N+9];

void Dfs1(int k,int fa){
  ord[++co]=k;ld[k]=co;
  d[k].fa=fa;
  d[k].dep=d[fa].dep+1;
  d[k].siz=1;
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^fa) {
	  Dfs1(e[i].y,k);
      d[k].siz+=d[e[i].y].siz;
      if (d[d[k].son].siz<d[e[i].y].siz) d[k].son=e[i].y;
	}
  rd[k]=co;
}

void Dfs2(int k,int top){
  d[k].top=top;
  if (d[k].son) Dfs2(d[k].son,top);
  for (int i=lin[k];i;i=e[i].next)
    if (e[i].y^d[k].fa&&e[i].y^d[k].son) Dfs2(e[i].y,e[i].y);
}

int Lca(int x,int y){
  while (d[x].top^d[y].top)
    d[d[x].top].dep>d[d[y].top].dep?x=d[d[x].top].fa:y=d[d[y].top].fa;
  return d[x].dep<d[y].dep?x:y;
}

void Bit_add(int t,int x,LL v){if (x==0) return;for (;x<=n;x+=x&-x) c[t][x]+=v;}
LL Bit_query(int t,int x){LL sum=0;for (;x;x-=x&-x) sum+=c[t][x];return sum;}
LL Bit_query(int t,int l,int r){return Bit_query(t,r)-Bit_query(t,l-1);}

void Add_chain(int x,int y,LL v){
  int lca=Lca(x,y);
  Bit_add(0,ld[x],v);Bit_add(0,ld[y],v);
  Bit_add(0,ld[lca],-v);Bit_add(0,ld[d[lca].fa],-v);
  Bit_add(1,ld[x],v*d[x].dep);Bit_add(1,ld[y],v*d[y].dep);
  Bit_add(1,ld[lca],-v*d[lca].dep);Bit_add(1,ld[d[lca].fa],-v*d[d[lca].fa].dep);
}

void Build(){for (int i=1;i<=n;++i) Add_chain(i,i,v[i]);}
LL Query_node(int x){return Bit_query(0,ld[x],rd[x]);}
LL Query_tree(int x){return Bit_query(1,ld[x],rd[x])-Bit_query(0,ld[x],rd[x])*(d[x].dep-1);}

Abigail into(){
  n=Ri();m=Ri();rot=Ri();
  int x,y;
  for (int i=1;i<=n;++i) v[i]=(LL)Ri();
  for (int i=1;i<n;++i){
  	x=Ri();y=Ri();
  	Ins(x,y);Ins(y,x);
  }
}

Abigail work(){
  Dfs1(rot,0);
  Dfs2(rot,rot);
  Build();
}

Abigail getans(){
  int opt,x,y,c;
  while (m--){
  	opt=Ri();
  	switch (opt){
	  case 1:
	  	x=Ri();y=Ri();c=Ri();
	  	Add_chain(x,y,(LL)c);
	  	break;
	  case 2:
	  	x=Ri();
	  	printf("%lld\n",Query_node(x));
	  	break;
	  case 3:
	  	x=Ri();
	  	printf("%lld\n",Query_tree(x));
	  	break;
	}
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值