您的位置:首页 > 其它

[BZOJ 4034][HAOI 2015] 树上操作 树链剖分+DFS序

2017-10-01 00:58 405 查看
题目传送门:【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 的权值,最后统计并求和即可。

所以这就是一道普通的树链剖分模板题。

下面附上代码:

[cpp] view plain copy print?#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: