1. 程式人生 > >BZOJ1036 樹的統計(樹鏈剖分+線段樹)

BZOJ1036 樹的統計(樹鏈剖分+線段樹)

【題目描述】

一棵樹上有n個節點,編號分別為1到n,每個節點都有一個權值w。我們將以下面的形式來要求你對這棵樹完成一些操作:

I. CHANGE u t : 把結點u的權值改為t

II. QMAX u v: 詢問從點u到點v的路徑上的節點的最大權值

III. QSUM u v: 詢問從點u到點v的路徑上的節點的權值和

注意:從點u到點v的路徑上的節點包括u和v本身

【輸入格式】

輸入的第一行為一個整數n,表示節點的個數。接下來n – 1行,每行2個整數a和b,表示節點a和節點b之間有一條邊相連。

接下來n行,每行一個整數,第i行的整數wi表示節點i的權值。

接下來1行,為一個整數q,表示操作的總數。

接下來q行,每行一個操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式給出。 

對於100%的資料,保證1<=n<=30000,0<=q<=200000;中途操作中保證每個節點的權值w在-30000到30000之間。

【輸出格式】

對於每個“QMAX”或者“QSUM”的操作,每行輸出一個整數表示要求輸出的結果。

【樣例輸入】

4

1 2

2 3

4 1

4 2 1 3

12

QMAX 3 4

QMAX 3 3

QMAX 3 2

QMAX 2 3

QSUM 3 4

QSUM 2 1

CHANGE 1 5

QMAX 3 4

CHANGE 3 6

QMAX 3 4

QMAX 2 4

QSUM 3 4

【樣例輸出】

4

1

2

2

10

6

5

6

5

16

【題目分析】

這個題大意就是:給定一棵樹,要求其支援單點修改,路徑查詢最大值和,路徑求和。

這個就是典型樹鏈剖分板題了,用線段樹維護重鏈資訊,對於單點修改就直接線上段樹中修改,對於路徑詢問直接線上段樹上進行區間查詢即可。(求路過大佬指教為何我的程式WA了QAQ)

【程式碼(emmm,等我檢查出來就更新)】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e4+10;
const int MAXM=6e4+10;
const int INF=0x3f3f3f3f;

int n,q,cnt,sz;
int head[MAXN],depth[MAXN],fat[MAXN],son[MAXN],siz[MAXN];
int top[MAXN],dfn[MAXN],tot,rec[MAXN];
int to[MAXM],nxt[MAXM];
int pos[MAXN],bl[MAXN];
int v[MAXN];

void Add(int x,int y)
{
	cnt++;
	nxt[cnt]=head[x];
	head[x]=cnt;
	to[cnt]=y;
}

void add(int x,int y)
{
	Add(x,y);
	Add(y,x);
}

void init()
{
	scanf("%d",&n);
	for(int i=1;i<n;++i)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
	}
	for(int i=1;i<=n;++i)
	  scanf("%d",&v[i]);
}

void dfs1(int x,int fa)
{
	if(x==1)
	  depth[x]=1;
	else
	  depth[x]=depth[fa]+1;
	siz[x]=1;
	for(int i=head[x];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa)
		{
			fat[v]=x;
			dfs1(v,x);
			siz[x]+=siz[v];
			if(siz[v]>son[x])
			  son[x]=v;
		}
	}
}

void dfs2(int x,int fa)
{
	dfn[x]=++tot;
	rec[tot]=x;
	if(x!=1)
	{
	    if(x==son[fa])
	      top[x]=top[fat[x]];
	    else
	  	  top[x]=x;
	}
	for(int i=head[x];i!=-1;i=nxt[i])
	{
		int v=to[i];
		if(v!=fa)
		  dfs2(v,x);
	}
}

int sum[MAXN<<2],maxx[MAXN<<2];

void push_up(int root)
{
	sum[root]=sum[root<<1]+sum[root<<1|1];
	maxx[root]=max(maxx[root<<1],maxx[root<<1|1]);
}

void build(int root,int l,int r)
{
	if(l==r)
	{
		sum[root]=maxx[root]=v[rec[l]];
		return ;
	}
	int mid=l+r>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	push_up(root);
}

void update(int root,int l,int r,int id,int key)
{
	if(l==r&&l==id)
	{
		sum[root]=maxx[root]=key;
		return ;
	}
	int mid=l+r>>1;
	if(id<=mid)
	  update(root<<1,l,mid,id,key);
	else
	  update(root<<1|1,mid+1,r,id,key);
	push_up(root);
}

int querysum(int root,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)
	  return sum[root];
	int mid=l+r>>1,ret=0;
	if(L<=mid)
	  ret+=querysum(root<<1,l,mid,L,R);
	if(R>mid)
	  ret+=querysum(root<<1|1,mid+1,r,L,R);
	return ret;
}

int querymax(int root,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)
	  return maxx[root];
	int mid=l+r>>1,ret=0;
	if(L<=mid)
	  ret=max(ret,querymax(root<<1,l,mid,L,R));
	if(R>mid)
	  ret=max(ret,querymax(root<<1|1,mid+1,r,L,R));
	return ret;
}

int findmax(int x,int y)
{
	int f1=top[x],f2=top[y],tmp=-INF;
	while(f1!=f2)
	{
		if(depth[f1]<depth[f2])
		  swap(f1,f2),swap(x,y);
		tmp=max(tmp,querymax(1,1,n,dfn[f1],dfn[x]));
		x=fat[f1],f1=top[x];
	}
	if(x==y)
	  return max(tmp,querymax(1,1,n,dfn[x],dfn[x]));
	if(depth[x]>depth[y])
	  swap(x,y);
	return max(tmp,querymax(1,1,n,dfn[x],dfn[y]));
}

int findsum(int x,int y)
{
	int f1=top[x],f2=top[y],tmp=0;
	while(f1!=f2)
	{
		if(depth[f1]<depth[f2])
		  swap(f1,f2),swap(x,y);
		tmp+=querysum(1,1,n,dfn[f1],dfn[x]);
		x=fat[f1],f1=top[x];
	}
	if(x==y)
	  return tmp+querysum(1,1,n,dfn[x],dfn[x]);
	if(depth[x]>depth[y])
	  swap(x,y);
	return tmp+querysum(1,1,n,dfn[x],dfn[y]);
}

int main()
{
	memset(head,-1,sizeof(head));
	init();
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);

	scanf("%d",&q);
	while(q--)
	{
		char s[20];
		int x,y;
		scanf("%s%d%d",s,&x,&y);
		if(s[0]=='C')
		  update(1,1,n,dfn[x],y);
		else
		{
			if(s[1]=='M')
			  printf("%d\n",findmax(x,y));
			else
			  printf("%d\n",findsum(x,y));
		}
	}
	return 0;
}