Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身
Input
输入的第一行为一个整数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之间。
Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
Sample Input
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
Sample Output
4
1
2
2
10
6
5
6
5
16
一看就知道是很裸的树剖模板题。这是我第一次写,没看大神模板,全靠自己yy,居然写出来了,感动。
我的做法比较傻逼,对于每次求和/最大值,我都是先算lca,然后把u,v往上提,这就导致了多出了预处理st表的过程以及求lca的过程(话说回来了,这样唯一的好处就是联系了一下lca的模板,对于第一次写lca的我来说也是非常有意义的),在每次求值时,也是分开求得,导致我的代码奇长无比,同时,这种做法的一个潜在弊端就是时间上是o((n+m) logn+n (logn)^2)
来自黄学长的做法,详见http://hzwer.com/2543.html
他的做法与我的做法最大的不同就在于他没有求lca,而是在求和/最大值时将u,v一起往上提,这样一来就节约了求lca的时间,同时代码量大大缩减,这种将两个相似过程合并的奇技淫巧在其他地方也有应用,看来还是得多看模板,多学科技
我的代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <queue>
#include <stack>
#include <map>
#include <vector>
#include <limits.h>
#include <malloc.h>
#include <ctype.h>
#include <float.h>
using namespace std;
const int maxn=30005;
const int oo=1000000005;
int i,n,m,u,v,len,cnt;
int head[maxn];
int deep[maxn];
int a[25];
int w[maxn];
int st[maxn][25];
int f[maxn];
char str[15];
int fa[maxn];
int son[maxn];
int siz[maxn];
int top[maxn];
int o[maxn];
struct edge{
int u,v,next;
edge(){u=v=0;next=-1;}
} e[100005];
struct node{
int l,r,sum,max;
struct node *lc,*rc;
node(){l=r=sum=max=0;lc=rc=NULL;}
};
node *cur;
void add_edge(int u,int v){
len++;
e[len].u=u;
e[len].v=v;
e[len].next=head[u];
head[u]=len;
}
void new_st(int x,int d,int f){
deep[x]=d;
st[x][0]=x;
st[x][1]=f;
int i=2;
while(a[i]<=d){
st[x][i]=st[st[st[x][i-1]][1]][i-1];
i++;
}
if((e[head[x]].next==-1)&&f)
return;
for(i=head[x];i!=-1;i=e[i].next)
if(e[i].v!=f)
new_st(e[i].v,d+1,x);
}
int lca(int u,int v){
if(deep[u]>deep[v])
swap(u,v);
int deld=deep[v]-deep[u];
for(i=23;i>=0;i--)
if(deld>=a[i]){
deld-=a[i];
v=st[st[v][i]][1];
}
if(u==v)
return -1;
for(i=23;i>=0;i--)
if(st[st[u][i]][1]!=st[st[v][i]][1]){
u=st[st[u][i]][1];
v=st[st[v][i]][1];
}
return st[u][1];
}
node* new_tree(int l,int r){
node *p=new node;
p->l=l;
p->r=r;
if(l==r){
p->sum=p->max=w[f[l]];
return p;
}
p->lc=new_tree(l,(l+r)/2);
p->rc=new_tree((l+r)/2+1,r);
p->sum=p->lc->sum+p->rc->sum;
p->max=max(p->lc->max,p->rc->max);
return p;
}
void print(node *p){
printf("l=%d r=%d:sum=%d max=%d\n",p->l,p->r,p->sum,p->max);
if(p->l!=p->r){
print(p->lc);
print(p->rc);
}
}
int q_sum(node *p,int l,int r){
if(p->l>=l&&p->r<=r)
return p->sum;
if(p->l==p->r)
return p->sum;
int tmp=0;
if(l<=p->lc->r)
tmp+=q_sum(p->lc,l,r);
if(r>=p->rc->l)
tmp+=q_sum(p->rc,l,r);
return tmp;
}
int q_max(node *p,int l,int r){
if(p->l>=l&&p->r<=r)
return p->max;
if(p->l==p->r)
return p->max;
int tmp=-oo;
if(l<=p->lc->r)
tmp=max(tmp,q_max(p->lc,l,r));
if(r>=p->rc->l)
tmp=max(tmp,q_max(p->rc,l,r));
return tmp;
}
void change(node *p,int x,int z){
if(p->l==p->r){
p->sum=p->max=z;
return;
}
if(x<=p->lc->r)
change(p->lc,x,z);
else
change(p->rc,x,z);
p->sum=p->lc->sum+p->rc->sum;
p->max=max(p->lc->max,p->rc->max);
}
int dfs1(int x,int f){
int ma=0,mi=-1,t;
fa[x]=f;
for(int i=head[x];i!=-1;i=e[i].next)
if(e[i].v!=f){
t=dfs1(e[i].v,x);
siz[x]+=t;
if(t>ma){
ma=t;
mi=e[i].v;
}
}
son[x]=mi;
siz[x]++;
return siz[x];
}
void dfs2(int x,int flag){
cnt++;
f[cnt]=x;
o[x]=cnt;
if(flag)
top[x]=top[fa[x]];
else
top[x]=x;
if(son[x]==-1)
return;
dfs2(son[x],1);
for(int i=head[x];i!=-1;i=e[i].next)
if(e[i].v!=son[x]&&e[i].v!=fa[x])
dfs2(e[i].v,0);
}
void work_sum(){
int tmp=0,zx;
if((zx=lca(u,v))==-1){
if(deep[u]>deep[v])
swap(u,v);
while(top[u]!=top[v]){
tmp+=q_sum(cur,o[top[v]],o[v]);
v=fa[top[v]];
}
tmp+=q_sum(cur,o[u],o[v]);
printf("%d\n",tmp);
return;
}
while(top[zx]!=top[u]){
tmp+=q_sum(cur,o[top[u]],o[u]);
u=fa[top[u]];
}
tmp+=q_sum(cur,o[zx],o[u]);
while(top[zx]!=top[v]){
tmp+=q_sum(cur,o[top[v]],o[v]);
v=fa[top[v]];
}
tmp+=q_sum(cur,o[zx],o[v]);
tmp-=q_sum(cur,o[zx],o[zx]);
printf("%d\n",tmp);
}
void work_max(){
int tmp=-oo,zx;
if((zx=lca(u,v))==-1){
if(deep[u]>deep[v])
swap(u,v);
while(top[u]!=top[v]){
tmp=max(tmp,q_max(cur,o[top[v]],o[v]));
v=fa[top[v]];
}
tmp=max(tmp,q_max(cur,o[u],o[v]));
printf("%d\n",tmp);
return;
}
while(top[zx]!=top[u]){
tmp=max(tmp,q_max(cur,o[top[u]],o[u]));
u=fa[top[u]];
}
tmp=max(tmp,q_max(cur,o[zx],o[u]));
while(top[zx]!=top[v]){
tmp=max(tmp,q_max(cur,o[top[v]],o[v]));
v=fa[top[v]];
}
tmp=max(tmp,q_max(cur,o[zx],o[v]));
printf("%d\n",tmp);
}
int main(){
scanf("%d",&n);
for(i=1;i<=n;i++)
head[i]=-1;
for(i=1,a[0]=1;i<=23;i++)
a[i]=a[i-1]*2;
for(i=1;i<n;i++){
scanf("%d %d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
new_st(1,1,0);
for(i=1;i<=n;i++)
scanf("%d",&w[i]);
dfs1(1,0);
cnt=0;
dfs2(1,0);
cur=new_tree(1,n);
scanf("%d\n",&m);
while(m--){
scanf("%s %d %d\n",str,&u,&v);
switch(str[1]){
case 'H':change(cur,o[u],v);break;
case 'M':work_max();break;
case 'S':work_sum();break;
}
}
return 0;
}
大神模板
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#define inf 0x7fffffff
#define N 30005
#define M 60005
using namespace std;
int n,q,cnt,sz;
int v[N],dep[N],size[N],head[N],fa[N];
int pos[N],bl[N];
struct data{int to,next;}e[M];
struct seg{int l,r,mx,sum;}t[100005];
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;
}
void ini()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
insert(x,y);
}
for(int i=1;i<=n;i++)scanf("%d",&v[i]);
}
void dfs1(int x)
{
size[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);
size[x]+=size[e[i].to];
}
}
void dfs2(int x,int chain)
{
int k=0;sz++;
pos[x]=sz;//分配x结点在线段树中的编号
bl[x]=chain;
for(int i=head[x];i;i=e[i].next)
if(dep[e[i].to]>dep[x]&&size[e[i].to]>size[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 k,int l,int r)//建线段树
{
t[k].l=l;t[k].r=r;
if(l==r)return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void change(int k,int x,int y)//线段树单点修改
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
if(l==r){t[k].sum=t[k].mx=y;return;}
if(x<=mid)change(k<<1,x,y);
else change(k<<1|1,x,y);
t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
t[k].mx=max(t[k<<1].mx,t[k<<1|1].mx);
}
int querysum(int k,int x,int y)//线段树区间求和
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
if(l==x&&y==r)return t[k].sum;
if(y<=mid)return querysum(k<<1,x,y);
else if(x>mid)return querysum(k<<1|1,x,y);
else {return querysum(k<<1,x,mid)+querysum(k<<1|1,mid+1,y);}
}
int querymx(int k,int x,int y)//线段树区间求最大值
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>1;
if(l==x&&y==r)return t[k].mx;
if(y<=mid)return querymx(k<<1,x,y);
else if(x>mid)return querymx(k<<1|1,x,y);
else {return max(querymx(k<<1,x,mid),querymx(k<<1|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;
}
int solvemx(int x,int y)
{
int mx=-inf;
while(bl[x]!=bl[y])
{
if(dep[bl[x]]<dep[bl[y]])swap(x,y);
mx=max(mx,querymx(1,pos[bl[x]],pos[x]));
x=fa[bl[x]];
}
if(pos[x]>pos[y])swap(x,y);
mx=max(mx,querymx(1,pos[x],pos[y]));
return mx;
}
void solve()
{
build(1,1,n);
for(int i=1;i<=n;i++)
change(1,pos[i],v[i]);
scanf("%d",&q);
char ch[10];
for(int i=1;i<=q;i++)
{
int x,y;scanf("%s%d%d",ch,&x,&y);
if(ch[0]=='C'){v[x]=y;change(1,pos[x],y);}
else
{
if(ch[1]=='M')
printf("%d\n",solvemx(x,y));
else
printf("%d\n",solvesum(x,y));
}
}
}
int main()
{
ini();
dfs1(1);
dfs2(1,1);
solve();
return 0;
}