bzoj1036-树链剖分模板


   剖分后的树有如下性质:
    性质1:如果(v,u)为轻边,则siz[u] * 2< siz[v];
    性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。


总之两个dfs

dfs1(int x,int f)   //f是x的父亲

枚举和x相邻的点的时候注意不等于f 才可以递归

  要维护的东西:

    dep[x] x节点深度

    siz[x] 以x节点为根的子树的大小

    son[x] 以x节点为根的子树中重儿子的编号

    fa[x] x节点的父亲

dfs2(int x,int tp)

   为x节点编号(线段树中的下标),并且求出每个点的top[x]

   这个时候优先向x的重儿子也就是son[x]向下递归,之后再对x的其他儿子递归,但是tp的值变成了其他儿子本身,不再是top[x]

建线段树

    注:如果题目中给的是边的权值,就变成深度较深的孩子的点权(之前对于一条边要看是否需要根据深度交换两个端点)

    和普通的线段树的建树,修改,询问都一样

最关键的部分:

     如何在不同的链之间移动?

     记f1 =top[u],f2 = top[v]。
     当f1<> f2时:不妨设dep[f1] >=dep[f2],那么就更新u到f1的父边的权值(logn),并使u = fa[f1]。
     当f1 =f2时:u与v在同一条重链上,若u与v不是同一点,就更新u到v路径上的边的权值(logn),否则修改完成;
     重复上述过程,直到修改完成。

inline int find(int va, int vb)
{
     int f1 = top[va], f2 = top[vb], tmp = 0;
     while (f1 != f2)
     {
           if (dep[f1] < dep[f2])
           { swap(f1, f2); swap(va, vb); }
           tmp = max(tmp, maxi(1, 1, z, w[f1], w[va]));
           va = fa[f1]; f1 = top[va];
     }
     if (va == vb) return tmp;
     if (dep[va] > dep[vb]) swap(va, vb);
     return max(tmp, maxi(1, 1, z, w[son[va]], w[vb]));  // maxi 是线段树的query
}


一开始没有看到负数,不开心

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define MAXN 30010
int head[MAXN];
struct node{
	int y,next;
}edge[2*MAXN];
int id[MAXN],son[MAXN],dep[MAXN],fa[MAXN],siz[MAXN],a[MAXN];
int l,x,y,n,q,tot;
char s[10];
int pre[MAXN],top[MAXN];
void add(int x,int y)
{
	l++;
	edge[l].y=y;
	edge[l].next=head[x];
	head[x]=l;
}
void dfs1(int x,int f)
{
	int y;
	fa[x]=f;
	son[x]=0;//the heaviest son
	siz[x]=1;
	for (int i=head[x];i!=-1;i=edge[i].next)
		if (edge[i].y!=f)
		{
			y=edge[i].y;
			dep[y]=dep[x]+1;
			dfs1(y,x);
			siz[x]+=siz[y];
			if (siz[son[x]]<siz[y])
				son[x]=y;
		}
}
void dfs2(int x,int tp)
{
	int y;
	top[x]=tp;
	id[x]=++tot;// tot is the total number
	pre[id[x]]=x;
	if (son[x]) dfs2(son[x],tp);
	for (int i=head[x];i!=-1;i=edge[i].next)
	 if (edge[i].y!=fa[x] && edge[i].y!=son[x])
	 {
		 y=edge[i].y;
		 dfs2(y,y);
	 }
}
struct point{
	int l,r,sum,max;
}tr[4*MAXN];
void updata(int p)
{
	tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
	tr[p].max=max(tr[p<<1].max,tr[p<<1|1].max);
}
void build(int p,int l,int r)
{
	tr[p].l=l;tr[p].r=r;
	if (l==r) {tr[p].sum=tr[p].max=a[pre[l]]; return ;}// find the backforward l in a[] to build the tree
	int mid=(l+r) >> 1;
	build(p<<1, l, mid);build(p<<1|1,mid+1,r);
	updata(p);
}
void change(int p,int x,int y)
{
	if (tr[p].l==x && tr[p].r==x)
	{
		tr[p].sum=tr[p].max=y;
		return ;
	}
	int mid=(tr[p].l+tr[p].r) >> 1;
	if (x<=mid) change(p<<1, x, y);
	if (x>mid) change(p<<1|1, x, y);
    updata(p);
}
int ask_max(int p,int l,int r)
{
	if (tr[p].l==l && tr[p].r==r)
	    return tr[p].max;
	int mid=(tr[p].l+tr[p].r) >> 1;
	if (r<=mid) return ask_max(p<<1, l, r);
	if (l>mid) return ask_max(p<<1|1, l, r);
	if (l<=mid && r>mid)
	{
		int s1=ask_max(p<<1, l, mid);
		int s2=ask_max(p<<1|1, mid+1, r);
		return max(s1,s2);
	}
}
int ask_sum(int p,int l,int r)
{
	if (tr[p].l==l && tr[p].r==r)
		return tr[p].sum;
	int mid=(tr[p].l+tr[p].r) >> 1;
	if (r<=mid) return ask_sum(p<<1, l, r);
	if (l>mid) return ask_sum(p<<1|1, l, r);
	if (l<=mid && r>mid)
	{
		int s1=ask_sum(p<<1, l, mid);
		int s2=ask_sum(p<<1|1, mid+1, r);
		return s1+s2;
	}
}
int find_max(int x, int y)
{
	int f1=top[x],f2=top[y],tmp=-0x3f3f3f;//有负数
	while (f1!=f2)
	{
		if (dep[f1]<dep[f2])
		{swap(f1,f2);swap(x,y);}
		tmp=max(tmp, ask_max(1,id[f1],id[x]));
		x=fa[f1];f1=top[x];
	}
	if (x==y) return max(tmp,ask_max(1,id[x],id[x]));
	if (dep[x]>dep[y]) swap(x,y);
	return max(tmp, ask_max(1, id[x], id[y]));
}
int find_sum(int x,int y)
{
	int f1=top[x], f2=top[y], tmp=0;
	while (f1!=f2)
	{
		if (dep[f1]<dep[f2])
		{ swap(f1,f2);swap(x,y);}
		tmp+=ask_sum(1,id[f1],id[x]);
		x=fa[f1];f1=top[x];
	}
	if (x==y) return tmp+ask_sum(1,id[x],id[y]);
	if (dep[x]>dep[y]) swap(x,y);
	return tmp+ask_sum(1,id[x], id[y]);
}
int main()
{
	scanf("%d", &n);
	memset(head,-1,sizeof(head));
	for (int i=1;i<n;i++)
	{
		scanf("%d%d", &x, &y);
		add(x,y);
		add(y,x);
	}
	for (int i=1;i<=n;i++) scanf("%d", &a[i]);
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);
	scanf("%d", &q);
	while (q--)
	{
		scanf("%s%d%d", s, &x, &y);
		if (s[0]=='C')  change(1,id[x],y); // use id[x] !!
        if (s[0]=='Q' && s[1]=='M') printf("%d\n", find_max(x,y));//x and y are two points
		if (s[0]=='Q' && s[1]=='S') printf("%d\n", find_sum(x,y));//if there is X types of queries, we need to write X parts for the queries
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值