关于轻重边及树链剖分该怎么写...

7 篇文章 0 订阅
5 篇文章 0 订阅

AC BZOJ1036 裸的树链模板

#include <cstdio>
#include <iostream>

#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>

#include <queue>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <list>

typedef unsigned int uint;
typedef long long int ll;
typedef unsigned long long int ull;
typedef double db;

using namespace std;

inline int getint()
{
	int res=0;
	char c=getchar();
	bool mi=false;
	while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
	while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
	return mi ? -res : res;
}


const int INF=(1<<30)-1;

int n;


int a[30050];


//Segment Tree
ll sum[240000];
int mx[240000];

void update(const int&x)
{
	sum[x]=sum[x<<1]+sum[x<<1|1];
	mx[x]=max(mx[x<<1],mx[x<<1|1]);
}

void Build(const int&x=1,const int&l=0,const int&r=n-1)
{
	if(l==r) { mx[x]=sum[x]=a[l]; return ; }
	int mid=(l+r)>>1;
	Build(x<<1,l,mid);
	Build(x<<1|1,mid+1,r);
	update(x);
}

int cp,cv;
void Change(const int&x=1,const int&l=0,const int&r=n-1)
{
	if(cp<l || r<cp) return ;
	if(l==r) { mx[x]=sum[x]=cv; return ; }
	int mid=(l+r)>>1;
	Change(x<<1,l,mid);
	Change(x<<1|1,mid+1,r);
	update(x);
}

int ql,qr;
int QueryMax(const int&x=1,const int&l=0,const int&r=n-1)
{
	if(qr<l || r<ql) return -INF;
	if(ql<=l && r<=qr) return mx[x];
	int mid=(l+r)>>1;
	return max(
	QueryMax(x<<1,l,mid),
	QueryMax(x<<1|1,mid+1,r));
}
ll QuerySum(int x=1,int l=0,int r=n-1)
{
	if(qr<l || r<ql) return 0;
	if(ql<=l && r<=qr) return sum[x];
	int mid=(l+r)>>1;
	return QuerySum(x<<1,l,mid) + QuerySum(x<<1|1,mid+1,r);
}

//End of Segment Tree

struct edge
{
	int in;
	edge*nxt;
}pool[80000];
edge*et=pool;
edge*eds[30050];
void addedge(int a,int b)
{
	et->in=a; et->nxt=eds[b]; eds[b]=et++;
	et->in=b; et->nxt=eds[a]; eds[a]=et++;
}
#define FOREACH_EDGE(i,j) for(edge*i=eds[j];i;i=i->nxt)

int c[30050],ctot; //chain code
int h[30050]; //head node of chain this node stands
bool t[30050]; //is this node a tail of a chain?
int f[30050]; //father node
int id[30050]; //location in segment
int dep[30050]; //depth

int DFS(int x,int cd)
{
	dep[x]=cd;
	int sum=0;
	int m=0;
	int p=-1;
	FOREACH_EDGE(i,x)
	if(i->in!=f[x])
	{
		f[i->in]=x;
		int h=DFS(i->in,cd+1);
		sum+=h;
		if(m<h)
		{
			m=h;
			p=i->in;
		}
	}
	
	if(p==-1) c[x]=ctot++,t[x]=true; //start a new chain
	else c[x]=c[p]; //Insert this node to a chain
	
	return sum+1;
}

inline int GetMax(int a,int b)
{
	int res=-INF;
	while(c[a]!=c[b])
	{
		if(dep[h[c[a]]]<dep[h[c[b]]]) swap(a,b);
		int&top=h[c[a]];
		ql=id[a];
		qr=id[top];
		res=max(res,QueryMax());
		a=f[top];
	}
	
	ql=min(id[a],id[b]);
	qr=max(id[a],id[b]);
	res=max(res,QueryMax());
	
	return res;
}

inline ll GetSum(int a,int b)
{
	ll res=0;
	while(c[a]!=c[b])
	{
		if(dep[h[c[a]]]<dep[h[c[b]]]) swap(a,b);
		int&top=h[c[a]];
		ql=id[a];
		qr=id[top];
		res+=QuerySum();
		a=f[top];
	}
	
	ql=min(id[a],id[b]);
	qr=max(id[a],id[b]);
	res+=QuerySum();
	
	return res;
}

inline void Edit(int a,ll p)
{
	cp=id[a];
	cv=p;
	Change();
}


int main()
{
	n=getint();
	for(int i=0;i<n-1;i++)
	addedge(getint()-1,getint()-1);
	
	f[0]=0;
	DFS(0,0);
	
	//assign nodes to the segment tree
	int base=0;
	for(int i=0;i<n;i++)
	if(t[i])
	{
		int x=i;
		while(true)
		{
			id[x]=base++;
			if(c[f[x]]==c[x] && x!=0) x=f[x];
			else break;
		}
		h[c[x]]=x;
	}
	
	for(int i=0;i<n;i++)
	a[id[i]]=getint();
	
	Build();
	
	//deal all query
	int q=getint();
	for(int d=0;d<q;d++)
	{
		char inp[8];
		scanf("%s",inp);
		
		switch(inp[3])
		{
			case 'X': //QMAX
			{
				int l=getint()-1;
				int r=getint()-1;
				printf("%d\n",GetMax(l,r));
			}
			break;
			case 'M': //QSUM
			{
				int l=getint()-1;
				int r=getint()-1;
				printf("%lld\n",GetSum(l,r));
			}
			break;
			case 'N': //CHANGE
			{
				int p=getint()-1;
				int v=getint();
				Edit(p,v);
			}
			break;
			default:break;
		}
		
	}
	
	return 0;
}


照着这个写!


写树链的大致思路:

1.线段树.

一棵全局线段树.......虽然牺牲了常数...但是至少不用去动态开树了=w=

2.构造轻重边.

这个是重点.

首先DFS记下: 1.父节点 2.是不是叶节点 3.所属链号

如果当前节点是叶节点,那么新开一条链.

如果当前节点不是叶节点,那么此节点所树链为节点最多的子树的根所属的链.

然后在DFS之外,用两层循环标记: 1.链头 2.每个节点在线段树中的位置.

一直从叶节点跑上去即可. 显然有多少个叶节点就有多少条链.

3.查询操作

查询(a,b)时保证a与b中a的所属链深度大于b的所属链深度.然后询问a到链头的值.

如果是边权....嗯.....我习惯把边权压到深度较大的那个节点......于是询问的时候:

链询问:询问a到链头的值; 然后再询问链头到它父节点(就是它本身,这个时候没必要理会)的值.

当a,b同属一条链时,还是令a的深度大于b的深度,然后询问a到b的前一个节点的值.





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值