洛谷原题
就是一道树链剖分的模板题
多谢fancy学姐,几乎是照着她改的,但是理解了
难点在2操作
用st[]记录当前节点在线段树上的标号
ed[]记录当前节点的子树上在线段树上的最大标号(这棵树上的节点标号肯定都在一个区间里)
修改时把单点修改和区间修改放一块了
上代码
#include<cstdio>
#include<vector>
#include<iostream>
using namespace std;
const int N=100001;
int n,m,tot,totw,x,y,k,f1;
long long ans;
int size[N],fa[N],dep[N],st[N],ed[N],son[N],top[N],a[N],b[N];
vector<int> to[N];
struct node{
int l,r,ls,rs;long long mark,sum;
}t[N*2];
void dfs(int x)//求出size[],fa[],dep[],son[]
{
size[x]=1;
for(int i=0;i<to[x].size();i++)
{
int y=to[x][i];
if(y==fa[x]) continue;
dep[y]=dep[x]+1;
fa[y]=x;
dfs(y);
size[x]+=size[y];
if(size[son[x]]<size[y])
{
son[x]=y;
}
}
}
void dfs2(int x)//求出st[],ed[],top[]
{
st[x]=ed[x]=++totw;
if(!son[x]) return;
int y=son[x];
top[y]=top[x];
dfs2(y);//先遍历重儿子,保证重链在一个区间上
for(int i=0;i<to[x].size();i++)//
{
y=to[x][i];
if(y==fa[x]||y==son[x])continue;
top[y]=y;
dfs2(y);
}
ed[x]=totw;//在每一个子树遍历一遍后把ed[]值赋为这个子树最后遍历的那个点的标号
}
void build(int root,int L,int R)
{
t[root].l=L;t[root].r=R;
if(L==R)
{
t[root].sum=b[L];
return;
}
int mid=(L+R)/2;
t[root].ls=++tot;build(t[root].ls,L,mid);
t[root].rs=++tot;build(t[root].rs,mid+1,R);
t[root].sum=t[t[root].ls].sum+t[t[root].rs].sum;
}
void pushdown(int root)
{
int L=t[root].ls,R=t[root].rs;
if(!L&&!R) return;
long long ma=t[root].mark;
t[root].mark=0;
t[L].mark+=ma;t[L].sum+=ma*(t[L].r-t[L].l+1);
t[R].mark+=ma;t[R].sum+=ma*(t[R].r-t[R].l+1);
}
void add(int root,int L,int R,int d)
{
if(t[root].mark) pushdown(root);
if(L<=t[root].l&&t[root].r<=R)
{
t[root].mark+=(long long)d;
t[root].sum+=(long long)(t[root].r-t[root].l+1)*d;
return;
}
int mid=(t[root].l+t[root].r)>>1;
if(L<=mid) add(t[root].ls,L,R,d);
if(R>mid) add(t[root].rs,L,R,d);
t[root].sum=t[t[root].ls].sum+t[t[root].rs].sum;
}
long long Sum(int root,int L,int R)
{
if(t[root].mark) pushdown(root);
if(L<=t[root].l&&t[root].r<=R) return t[root].sum;
int mid=(t[root].l+t[root].r)>>1;
long long sum=0;;
if(L<=mid) sum+=Sum(t[root].ls,L,R);
if(R>mid) sum+=Sum(t[root].rs,L,R);
return sum;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
to[x].push_back(y);//vector大法好
to[y].push_back(x);
}
dep[1]=1;
top[1]=1;
dfs(1);dfs2(1);//两遍dfs
for(int i=1;i<=n;i++)
{
b[st[i]]=a[i];
}
tot=1;
build(1,1,n);//线段树
for(int i=1;i<=m;i++)
{
scanf("%d%d",&k,&x);
if(k==1)
{
scanf("%d",&y);
add(1,st[x],st[x],y);
}
else
if(k==2)
{
scanf("%d",&y);
add(1,st[x],ed[x],y);
}
else
{
ans=0;f1=top[x];
while(f1!=1)
{
ans+=Sum(1,st[f1],st[x]);//重链在线段树上是连续的,一次跳一个链
x=fa[f1];f1=top[x];
}
ans+=Sum(1,1,st[x]);
printf("%lld\n",ans);
}
}
}
本文针对洛谷的一道树链剖分题目进行了解析,详细介绍了使用线段树实现的操作方法,包括单点修改、区间修改等关键步骤,并通过具体代码实现了树的遍历与查询。
159

被折叠的 条评论
为什么被折叠?



