题目传送门:【BZOJ 4034】
题目大意:有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
题目分析:
(经实验证明,本人的查错能力越发低下,又花了三个小时来做这道题)
由题,结合树链剖分型题目的性质,容易看出,这是一道树链剖分的裸题。
我们先对这棵树进行树链剖分,然后在这之上建立一棵线段树。
对于操作 1,直接找到 x 在线段树中的位置(单点修改);
对于操作 2,根据 DFS 序的性质,可以发现,一个点及其子树的所有点在线段树中均为连续的一部分,所以直接根据点 x 的 DFS 序进行区间修改即可;
对于操作 3,由于 LCA( x , 1 ) == 1,所以先对 x 点从轻边边跳 father 边查询之后,再查询它到根 1 的权值,最后统计并求和即可。
所以这就是一道普通的树链剖分模板题。
下面附上代码:
- #include<cstdio>
- #include<algorithm>
- #define ls (nd<<1)
- #define rs (nd<<1|1)
- #define Root 1
- typedef long long LL;
- const int MX=200005;
- const int INF=1000005; //初始化懒标记时的值
- struct Edge{
- int to,next;
- }edge[MX*2];
- int n,m,now=0,_index=0, L , R , pos ,line[MX],head[MX],seq[MX];
- int depth[MX],size[MX],fa[MX],son[MX],top[MX],in[MX],out[MX];
- struct SegTree{
- LL sum,flag;
- }seg[MX*4];
- inline void adde(int u,int v){
- edge[++now].to=v;
- edge[now].next=head[u];
- head[u]=now;
- }
- void dfs1(int u){
- size[u]=1;
- for (int i=head[u];i;i=edge[i].next){
- int v=edge[i].to;
- if (fa[u]==v) continue;
- fa[v]=u;
- depth[v]=depth[u]+1;
- dfs1(v);
- if (size[v]>size[son[u]]) son[u]=v;
- size[u]+=size[v];
- }
- }
- void dfs2(int u,int tp){
- top[u]=tp;
- seq[++_index]=u;
- in[u]=_index;
- if (son[u]) dfs2(son[u],tp);
- for (int i=head[u];i;i=edge[i].next){
- int v=edge[i].to;
- if (fa[u]==v || son[u]==v) continue;
- dfs2(v,v);
- }
- out[u]=_index;
- }
- /*————————–Dividing Line————————–*/
- void build(int nd,int lf,int rt){
- if (lf==rt) seg[nd].sum=line[seq[lf]];
- else {
- int mid=(lf+rt)>>1;
- build(ls,lf,mid);
- build(rs,mid+1,rt);
- seg[nd].sum=seg[ls].sum+seg[rs].sum;
- }
- }
- void pushdown(int nd,int lf,int rt){
- if (seg[nd].flag && lf!=rt){
- int mid=(lf+rt)>>1;
- seg[ls].flag+=seg[nd].flag;
- seg[rs].flag+=seg[nd].flag;
- seg[ls].sum+=seg[nd].flag*(mid-lf+1);
- seg[rs].sum+=seg[nd].flag*(rt-mid);
- seg[nd].flag=0;
- }
- }
- void add(int nd,int lf,int rt,int val){//option. 1
- pushdown(nd,lf,rt);
- if (lf==rt){
- seg[nd].sum+=val;
- return;
- }
- int mid=(lf+rt)>>1;
- if (pos<=mid) add(ls,lf,mid,val);
- else add(rs,mid+1,rt,val);
- seg[nd].sum=seg[ls].sum+seg[rs].sum;
- }
- void add_tree(int nd,int lf,int rt,int val){//option. 2
- if (L>R) return;
- pushdown(nd,lf,rt);
- if (L<=lf && rt<=R){
- seg[nd].sum+=(LL)val*(rt-lf+1);
- seg[nd].flag+=(LL)val;
- return;
- }
- int mid=(lf+rt)>>1;
- if (L<=mid) add_tree(ls,lf,mid,val);
- if (R>mid) add_tree(rs,mid+1,rt,val);
- seg[nd].sum=seg[ls].sum+seg[rs].sum;
- }
- LL query(int nd,int lf,int rt){//option. 3
- if (L>R) return 0;
- pushdown(nd,lf,rt);
- if (L<=lf && rt<=R)
- return seg[nd].sum;
- int mid=(lf+rt)>>1; LL ans=0;
- if (L<=mid) ans+=query(ls,lf,mid);
- if (R>mid) ans+=query(rs,mid+1,rt);
- return ans;
- }
- LL query(int u,int v){
- LL ans=0;
- while (top[u]!=top[v]){
- if (depth[top[u]]<depth[top[v]]) std::swap(u,v);
- L=in[top[u]],R=in[u];
- ans+=query(Root,1,n);
- u=fa[top[u]];
- }
- if (depth[u]<depth[v]) std::swap(u,v);
- L=in[v],R=in[u];
- ans+=query(Root,1,n);
- return ans;
- }
- /*————————–Dividing Line————————–*/
- int main(){
- int a,b,opt;
- scanf(”%d%d”,&n,&m);
- for (int i=1;i<=n;i++)
- scanf(”%d”,&line[i]);
- for (int i=1;i<n;i++){
- scanf(”%d%d”,&a,&b);
- adde(a,b),adde(b,a);
- }
- depth[1]=1,fa[1]=1;
- dfs1(1);
- dfs2(1,1);
- build(Root,1,n);
- for (int i=1;i<=m;i++){
- scanf(”%d”,&opt);
- if (opt==1){
- scanf(”%d%d”,&a,&b);
- pos=in[a]; add(Root,1,n,b);
- }
- if (opt==2){
- scanf(”%d%d”,&a,&b);
- L=in[a],R=out[a]; add_tree(Root,1,n,b);
- }
- if (opt==3){
- scanf(”%d”,&a);
- printf(”%lld\n”,query(a,1));
- }
- }
- return 0;
- }