题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966
题目描述:树链剖分模板题,给你一颗树,有三种操作:
I: c1 到 c2 的点加上k
D:c1 到 c2 的点减去k
Q:查询某个点的值
题目做法:进行树链剖分然后就是线段树的操作,为了避免炸内存,图的存储用链式前向星进行优化。
链式前向星学习链接:https://blog.youkuaiyun.com/lookqaq/article/details/81304637
链式前向星:head[i]表示以i为起点的最后一条边的位置,next表示与第i条边同起点的上一条边的位置,to表示第i条边的终点。
树链剖分学习链接:https://www.cnblogs.com/ivanovcraft/p/9019090.html
顺便复习一下树链剖分:树链剖分就是将树进行轻重链剖分,从而转化为区间问题,运用线段树或者树状数组高效处理区间问题。主要代码是两遍dfs,第一遍dfs可以得到当前节点的父亲结点(fa数组)、当前结点的深度值(d数组)、当前结点的子结点数量(size数组)、当前结点的重儿子(son数组)。第二遍dfs的时候则可以将各个重结点连接成重链,轻节点连接成轻链,并且将重链(其实就是一段区间)用数据结构(一般是树状数组或线段树)来进行维护,并且为每个节点进行编号,其实就是dfs在执行时的顺序(num_id数组),以及当前节点所在链的起点(top数组),还有当前节点在树中的位置(id_num数组)。
复杂度分析:可以证明树链剖分的时间复杂度为O(nlog^2n)。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+10;
struct node{
int l;
int r;
int lazy;
int sum;
}tree[maxn<<2];
struct edge{
int next;
int to;
}e[maxn*2];
int v[maxn],head[maxn],id_num[maxn],num_id[maxn],top[maxn],size[maxn],fa[maxn],son[maxn],d[maxn],cnt;
void add(int u,int v){
e[++cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt;
}
void dfs1(int now,int f,int depth){
d[now] = depth;
size[now] = 1;
fa[now] = f;
for(int i = head[now];i;i = e[i].next){
int v = e[i].to;
if(v != f){
dfs1(v,now,depth+1);
size[now] += size[v];
if(size[v] > size[son[now]]){
son[now] = v;
}
}
}
}
void dfs2(int now,int rt){
top[now] = rt;
id_num[++cnt] = now;
num_id[now] = cnt;
if(!son[now])return ;
dfs2(son[now],top[now]);
for(int i = head[now];i;i = e[i].next){
int v = e[i].to;
if(v != son[now] && v != fa[now]){
dfs2(v,v);
}
}
}
void pushup(int rt){
tree[rt].sum = tree[rt*2].sum + tree[rt*2+1].sum;
}
void pushdown(int rt){
if(tree[rt].lazy){
tree[rt*2].lazy += tree[rt].lazy;
tree[rt*2+1].lazy += tree[rt].lazy;
tree[rt*2].sum += (tree[rt*2].r-tree[rt*2].l+1)*tree[rt].lazy;
tree[rt*2+1].sum += (tree[rt*2+1].r-tree[rt*2+1].l+1)*tree[rt].lazy;
tree[rt].lazy = 0;
}
}
void build(int rt,int l,int r){
if(l == r){
tree[rt].l = tree[rt].r = l;
tree[rt].lazy = 0;
tree[rt].sum = v[id_num[l]];
return ;
}
int mid = (l+r)/2;
tree[rt].r = r,tree[rt].l = l,tree[rt].lazy = 0;
build(rt*2,l,mid);build(rt*2+1,mid+1,r);
pushup(rt);
}
void update(int l,int r,int c,int rt){
if(tree[rt].r < l||tree[rt].l > r)return ;
if(tree[rt].l >= l&&tree[rt].r <= r){
tree[rt].sum +=(tree[rt].r-tree[rt].l+1)*c;
tree[rt].lazy += c;
return ;
}
pushdown(rt);
int mid = (tree[rt].l+tree[rt].r)/2;
if(l <= mid)update(l,r,c,rt*2);
if(mid < r) update(l,r,c,rt*2+1);
}
int query(int l,int r,int rt){
int res = 0;
if(tree[rt].r < l||tree[rt].l > r)return 0;
if(tree[rt].l >= l&&tree[rt].r <= r){
return tree[rt].sum;
}
pushdown(rt);
int mid = (tree[rt].l+tree[rt].r)/2;
if(l <= mid) res += query(l,r,rt*2);
if(mid < r) res += query(l,r,rt*2+1);
return res;
}
void updates(int x,int y,int c){
while(top[x] != top[y]){
if(d[top[x]] < d[top[y]]){
update(num_id[top[y]],num_id[y],c,1);
y = fa[top[y]];
}
else{
update(num_id[top[x]],num_id[x],c,1);
x = fa[top[x]];
}
}
if(d[x] < d[y]){
swap(x,y);
}
update(num_id[y],num_id[x],c,1);
}
void init(){
memset(head,0,sizeof(head));
memset(son,0,sizeof(son));
cnt = 0;
}
int main(){
int n,m,p;
while(scanf("%d%d%d",&n,&m,&p) != EOF){
init();
for(int i = 1;i <= n;++i)
{
scanf("%d",&v[i]);
}
int from,to;
for(int i = 1;i <= m;++i)
{
scanf("%d%d",&from,&to);
add(from,to);
add(to,from);
}
dfs1(1,1,1);
cnt = 0;dfs2(1,1);
//for(int i = 1;i <= n; i++)printf("%d ",id_num[i]);
build(1,1,n);
char op[2];
int x,y,z;
for(int i = 0;i < p;++i)
{
scanf("%s",op);
if(op[0] == 'I')
{
scanf("%d%d%d",&x,&y,&z);
updates(x,y,z);
}
else if(op[0] == 'D')
{
scanf("%d%d%d",&x,&y,&z);
updates(x,y,-z);
}
else
{
scanf("%d",&x);
printf("%d\n",query(num_id[x],num_id[x],1));
}
}
}
return 0;
}