树链剖分

学习计划如下
1.线段树学好
2.dfs序学好
3.脑子修好
补坑

课件
应该很详细了吧。
总的来说,先把树剖分成轻边和重边,在修改路径权值时,不断靠到同一条重链上,最后用线段树等经济结构维护重链,将修改和查询从\(O(n)\)降到\(O(logN)\)
轻边和重边一视同仁,全部加到线段树里面,轻边被视为很小的重边,复杂度均摊为\(O(log(N)\)
用途,维护树的信息。
1:最短路树割掉一些边的次短路。
其他的以后碰到再填吧

代码老惯例,等能随便敲出来再贴吧。

模板敲出来啦~~

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstdlib>
#include<stack>
using namespace std;
const int maxn = 400000;
struct Edge{int to,next;}e[maxn];
struct seg{int l,r,sum,mx;}t[maxn];
int fa[maxn],bl[maxn],dep[maxn],sz[maxn],v[maxn],head[maxn],pos[maxn];
int cnt = 0,tot;
void insert(int u,int v)
{
	e[++cnt].to = v;e[cnt].next = head[u];head[u] = cnt;
	e[++cnt].to = u;e[cnt].next = head[v];head[v] = cnt;
}
int N,M,R,P;
void gi(int &x){
	x = 0;int f = 1;char c = getchar();
	while(c < '0'||c > '9'){if(c == '-')f = -1;c = getchar();}
	while('0' <= c&&c <= '9') x = x*10+c-'0',c = getchar();
	x *= f;
}
void init()
{
	gi(N);
	for(int i = 1;i < N;i++){
		int u,v;
		gi(u);gi(v);
		insert(u,v);
	}
	for(int i = 1;i <= N;i++) gi(v[i]);

}
void dfs1(int x)
{
	sz[x] = 1;
	for(int i = head[x];i;i = e[i].next)
	{
		if(e[i].to == fa[x]) continue;
		dep[e[i].to] = dep[x]+1;
		fa[e[i].to] = x;
		dfs1(e[i].to);
		sz[x] += sz[e[i].to];
	}
}
void dfs2(int x,int chain)
{
	int k = 0;tot++;
	bl[x] = chain;
	pos[x] = tot;
	for(int i = head[x];i;i = e[i].next)
		if(dep[e[i].to] > dep[x]&&sz[e[i].to] > sz[k])
			k = e[i].to;
	if(k == 0) return;
	dfs2(k,chain);
	for(int i = head[x];i;i = e[i].next)
		if(dep[e[i].to] > dep[x]&&k != e[i].to)
			dfs2(e[i].to,e[i].to);
}
void build(int o,int l,int r)
{
	t[o].l = l;t[o].r = r;
	if(l == r) return;
	int mid = l+(r-l)/2;
	build(o*2,l,mid);
	build(o*2+1,mid+1,r);
}
void change(int o,int x,int v)
{
	int l = t[o].l,r = t[o].r;
	int mid = l+(r-l)/2;
	if(l == r){t[o].sum = t[o].mx = v;return;}
	if(x <= mid) change(o*2,x,v);
	else change(o*2+1,x,v);
	t[o].sum = t[o*2].sum+t[o*2+1].sum;
	t[o].mx = max(t[o*2].mx,t[o*2+1].mx);
}
int querysum(int o,int x,int y)
{
	int l = t[o].l,r = t[o].r;
	int mid = l+(r-l)/2;
	if(l == x&&r == y) return t[o].sum;
	if(y <= mid) return querysum(o*2,x,y);
	else if(x > mid) return querysum(o*2+1,x,y);
	else return querysum(o*2,x,mid)+querysum(o*2+1,mid+1,y);
}
int querymx(int o,int x,int y)
{
	int l = t[o].l,r = t[o].r;
	int mid = l+(r-l)/2;
	if(l == x&&r == y) return t[o].mx;
	if(y <= mid) return querymx(o*2,x,y);
	else if(x > mid) return querymx(o*2+1,x,y);
	else return max(querymx(o*2,x,mid),querymx(o*2+1,mid+1,y));
	
}
int solvesum(int x,int y)
{
	int sum = 0;
	while(bl[x] !=bl[y])
	{
		if(dep[bl[x]] < dep[bl[y]]) swap(x,y);	
		sum += querysum(1,pos[bl[x]],pos[x]);
		x = fa[bl[x]];
	}
	if(pos[x] > pos[y]) swap(x,y);
	sum += querysum(1,pos[x],pos[y]);
	return sum;
}
const int INF = ~0U>>1;
int solvemx(int x,int y)
{
	int M = -INF;
	while(bl[x] != bl[y]){
		if(dep[bl[x]] < dep[bl[y]]) swap(x,y);
		M = max(M,querymx(1,pos[bl[x]],pos[x]));
		x = fa[bl[x]];
	}
	if(pos[x] > pos[y]) swap(x,y);
	M = max(M,querymx(1,pos[x],pos[y]));
	return M;
}
void buildseg()
{
	build(1,1,N);
	for(int i = 1;i <= N;i++)
		change(1,pos[i],v[i]);
}
void solve()
{
	char s[10];
	gi(M);int x,y;
	for(int i = 0;i < M;i++)
	{
		scanf("%s",s);
		gi(x),gi(y);
		if(s[1] == 'M'){
			printf("%d\n",solvemx(x,y));
		}else if(s[1] == 'S'){
			printf("%d\n",solvesum(x,y));
		}else if(s[1] == 'H'){
			v[x] = y;
			change(1,pos[x],y);
		}
	}
}
int main()
{
	init();	
	dfs1(1);
	dfs2(1,1);
	buildseg();
	solve();
	return 0;
}

虽然应该还能优化。不过也还不错啦。
谢谢hzwer的博客

注意:
修改线段树的值,不是节点的标号,板子里是修改\(pos[x]\)而不是\(x\)
好像没啦
完全没有想象中那么高大上啊。

posted @ 2017-05-19 15:25  rsqppp  阅读(184)  评论(0)    收藏  举报