树链剖分问题

        树链剖分,英文名字为heavy path decomposition或heavy-light decomposition。它并不是一个复杂的算法或者数据结构,而是一种对树进行分解的技术。他把树分解为为多个不相交的顶点的集合,将一棵树的问题拆成不超过O(logn)个链的问题来处理。换句话说,树链剖分只是各种数据结构或算法在树上的推广。诸如,求树上的两个点之间的路径上的权值的最大值、最小值、和等问题均可以使用树链剖分来处理。


        树链剖分的第一步当然是对树进行轻重边的划分。定义size[x]为以x为根的子树的节点个数。

        重儿子:size[v]为u的子节点中size值最大的,那么v就是u的重儿子。
        轻儿子:v的其它子节点。
        重边:点v与其重儿子的连边。
        轻边:点v与其轻儿子的连边。
        重链:由重边连成的路径。
        轻链:由轻边连成的路径。

 
        当然,关于这个它有两个重要的性质:
        (a)轻边(u,v)中,size[v]*2 <= size[u]
        (b)从根到某一点的路径上,不超过logn条轻边和不超过logn条重边。


CodeChef201609问题JTREE

(1)问题描述:

(2)要点:

(3)代码:

unsigned long long slove_fast(const vector< vector<ticket_t> >& tickets,const unsigned int* parent,size_t size,unsigned int index,unsigned int ticket)
{
	static bool init = false;
	static vector<unsigned long long> quick_results;
	if(!init)
	{
		CTreeAncestor ta;														// O(sqrt(n))求出index的offset代祖宗
		ta.init(parent,size,1);

		unsigned int *out = new unsigned int[size];
		size_t outsize = CTreeUtil::from_top_to_bottom(parent,out,size,1);		// 从有根树的宽度优先遍历

		typedef vhld_segtree_t< paqmin_value_t,paqmin_value_t > CHLDSegTree;
		CHLDSegTree st;st.init(parent,size,1);

		quick_results.resize(size+1,(unsigned long long)(-1));
		quick_results[1] = 0;
		st.update(1,0);

		for(size_t i = 1;i < outsize;++i)
		{
			size_t u = out[i];
			unsigned long long mincost = 100000000000000000LL;
			for(size_t j = 0;j < tickets[u].size();++j)
			{
				size_t ancestor = ta.ancestor(u,tickets[u][j].ticket);
				if(0 == ancestor) ancestor = 1;
				unsigned long long ret = st.query(parent[u],ancestor).minv;

				unsigned long long minv = ret;
				minv += tickets[u][j].cost;
				if(minv < mincost) mincost = minv;
			}
			quick_results[u] = mincost;
			st.update(u,mincost);
		}
		delete[] out;
		init = true;
	}
	return quick_results[index];
}

//
// 显然,对于i < k,有f(n,i) >= f(n,k)
//
// f(n,0) = c(n) + min{ f(n-1,tn-1),f(n-2,tn-2),...,f(n-tn,0) }
// f(n,i) = min{ f(n,0),f(n-1,i-1) } <= f(n,0)
// 因此有,f(n,i) <= f(n-1,i-1) =>	f(n,0) = c(n) + f(n-1,tn-1)
//
// 可以进一步推导出;
// 若i >= n,则f(n,i) = min { f(n,0),f(n-1,0),f(n-2,i-2) } = ... = min { f(n,0),f(n-1,0),...f(1,0),f(0,i-n) }
// 若i < n,f(n,i) = min { f(n,0),f(n-1,0),f(n-2,i-2) } = ... = min { f(n,0),f(n-1,0),...f(1,0),f(n-i,0) }

int main()
{
	static const unsigned int maxn = 100000;
	static const unsigned int manm = 100000;
	unsigned int parent[maxn+1] = { 0 };
	unsigned int n = 0,m = 0;scanf("%d%d",&n,&m);
	vector< vector<ticket_t> > tickets;
	tickets.clear();tickets.resize(n+1);
	for(unsigned int i = 1;i < n;++i)
	{
		unsigned int a = 0,b = 0;scanf("%d%d",&a,&b);
		parent[a] = b;
	}
	for(unsigned int i = 0;i < m;++i)
	{
		unsigned int v = 0,k = 0,w = 0;scanf("%d%d%d",&v,&k,&w);
		ticket_t t;
		t.cost = w;t.ticket = k;
		tickets[v].push_back(t);
	}
	unsigned int query = 0;scanf("%d",&query);
	for(unsigned int iq = 0;iq < query;++iq)
	{
		unsigned int v = 0;scanf("%d",&v);
		unsigned long long ans = slove_fast(tickets,parent,n+1,v,0);
		printf("%llu\n",ans);
	}
	return 0;
}


HDU3966




SPOJ375


参考文献:

(1)https://en.wikipedia.org/wiki/Heavy_path_decomposition

(2)http://wcipeg.com/wiki/Heavy-light_decomposition

(3)https://blog.anudeep2011.com/heavy-light-decomposition/

(4)https://quartergeek.com/summary-of-heavy-light-decomposition/

(5)http://blog.sina.com.cn/s/blog_7a1746820100wp67.html

(6)http://blog.csdn.net/y990041769/article/details/40348013

(10)http://blog.csdn.net/acdreamers/article/details/10591443

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include <cstdio> #include <iostream> #include <vector> #define N 30003 #define INF 2147483647 using namespace std; int n,f[N][20],dep[N],siz[N],son[N],top[N],tot,pos[N],w[N]; int Max[N*4],Sum[N*4]; vector <int> to[N]; void dfs1(int x){ siz[x]=1; int sz=to[x].size(); for(int i=0;i<sz;++i){ int y=to[x][i]; if(y==f[x][0])continue; f[y][0]=x; dep[y]=dep[x]+1; dfs1(y); siz[x]+=siz[y]; if(siz[y]>siz[son[x]])son[x]=y; } } void dfs2(int x,int root){ top[x]=root; pos[x]=++tot; if(son[x])dfs2(son[x],root); int sz=to[x].size(); for(int i=0;i<sz;++i){ int y=to[x][i]; if(y==f[x][0] || y==son[x])continue; dfs2(y,y); } } void update(int k,int l,int r,int P,int V){ if(l==r){ Max[k]=Sum[k]=V; return; } int mid=(l+r)>>1; if(P<=mid)update(k*2,l,mid,P,V); else update(k*2+1,mid+1,r,P,V); Max[k]=max(Max[k*2],Max[k*2+1]); Sum[k]=Sum[k*2]+Sum[k*2+1]; } void up(int &x,int goal){ for(int i=15;i>=0;--i) if(dep[f[x][i]]>=goal)x=f[x][i]; } int lca(int x,int y){ if(dep[x]>dep[y])up(x,dep[y]); if(dep[x]<dep[y])up(y,dep[x]); if(x==y)return x; for(int i=15;i>=0;--i) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } int getm(int k,int l,int r,int L,int R){ if(L<=l && r<=R)return Max[k]; int res=-INF,mid=(l+r)>>1; if(L<=mid)res=max(res,getm(k*2,l,mid,L,R)); if(R>mid)res=max(res,getm(k*2+1,mid+1,r,L,R)); return res; } int gets(int k,int l,int r,int L,int R){ if(L<=l && r<=R)return Sum[k]; int res=0,mid=(l+r)>>1; if(L<=mid)res+=gets(k*2,l,mid,L,R); if(R>mid)res+=gets(k*2+1,mid+1,r,L,R); return res; } int main(){ scanf("%d",&n); for(int i=1,a,b;i<n;++i){ scanf("%d%d",&a,&b); to[a].push_back(b); to[b].push_back(a); } dep[1]=1; dfs1(1); dfs2(1,1); for(int i=1;i<=15;++i) for(int j=1;j<=n;++j)f[j][i]=f[f[j][i-1]][i-1]; for(int i=1;i<=n;++i){ scanf("%d",&w[i]); update(1,1,n,pos[i],w[i]); } int q; scanf("%d",&q); while(q--){ char s[10]; int u,v,t; scanf("%s",s); if(s[1]=='H'){ scanf("%d%d",&u,&t); w[u]=t; update(1,1,n,pos[u],t); } if(s[1]=='M'){ scanf("%d%d",&u,&v); int ans=-INF,t=lca(u,v); for(int i=u;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans=max(ans,getm(1,1,n,pos[top[i]],pos[i])); else{ ans=max(ans,getm(1,1,n,pos[t],pos[i])); break; } for(int i=v;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans=max(ans,getm(1,1,n,pos[top[i]],pos[i])); else{ ans=max(ans,getm(1,1,n,pos[t],pos[i])); break; } printf("%d\n",ans); } if(s[1]=='S'){ scanf("%d%d",&u,&v); int ans=0,t=lca(u,v); for(int i=u;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans+=gets(1,1,n,pos[top[i]],pos[i]); else{ ans+=gets(1,1,n,pos[t],pos[i]); break; } for(int i=v;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans+=gets(1,1,n,pos[top[i]],pos[i]); else{ ans+=gets(1,1,n,pos[t],pos[i]); break; } printf("%d\n",ans-w[t]); } } }
预处理 第一遍dfs求出树每个结点的深度deep[x],其为根的子树大小size[x] 以及祖先的信息fa[x][i]表示x往上距离为2^i的祖先 第二遍dfs ž根节点为起点,向下拓展构建重链 选择最大的一个子树的根继承当前重链 其余节点,都以该节点为起点向下重新拉一条重链 ž给每个结点分配一个位置编号,每条重链就相当于一段区间,用数据结构去维护。 把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可 修改操作 ž1、单独修改一个点的权值 根据其编号直接在数据结构中修改就行了。 2、修改点u和点v的路径上的权值 (1)若u和v在同一条重链上 直接用数据结构修改pos[u]至pos[v]间的值。 (2)若u和v不在同一条重链上 一边进行修改,一边将u和v往同一条重链上靠,然后就变成了情况(1)。 伪代码 CHANGE (x, y ,d) while top[x]≠top[y] do if dep[top[x]]<dep[top[y]] then SWAP(x,y), SWAP (gx,gy) CHANGE-IT(tid[top[x]],tid[x],d) fa[x]→x if dep[x]>dep[y] then SWAP (x,y) CHANGE-IT(tid[x],tid[y],d) //CHANGE-IT(l,r,d)为数据结构的修改操作:将区间[l,r]上的所有权值改为d 查询操作 ž查询操作的分析过程同修改操作 伪代码 QUERY (x, y) while top[x]≠top[y] do if dep[top[x]]<dep[top[y]] then SWAP (x,y), SWAP (gx,gy) QUERY-IT(tid[top[x]],tid[x]) fa[x]→x if dep[x]>dep[y] then SWAP (x,y) QUERY-IT(tid[x],tid[y]) //QUERY-IT(l,r)为数据结构的查询操作, 题目不同,选用不同的数据结构来维护值,通常有线段树和splay [2]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值