首触树链剖分

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 16525   Solved: 6719
[ Submit][ Status][ Discuss]

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

HINT

Source


#include<cstdio>

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 30001
//segment tree
int l[3*N],r[3*N],qsum[3*N],fid[3*N],id[3*N],pos,qmax[3*N];
//shulianpoufen
int fa[N],top[N],size[N],n,m,zson[N],depth[N];
//bianbiao
int fir[N],next[2*N],to[2*N],tot;
int x,y,q,val[N];
//bian GO FIRST
void add(int u,int v)
{
to[++tot]=v;next[tot]=fir[u];fir[u]=tot;
}
void dfs(int father,int deep,int x)
{
size[x]=1;fa[x]=father;depth[x]=deep;
zson[x]=0;
for(int i=fir[x];i;i=next[i])
{
int st=to[i];
if(st==fa[x]) continue;
dfs(x,deep+1,st);
size[x]+=size[st];
if (zson[x]==0||size[st]>size[zson[x]])
zson[x]=st;
}
}
void getpos(int x,int sp)
{
top[x]=sp;
id[x]=++pos;fid[id[x]]=x;if(zson[x]==0) return;
getpos(zson[x],sp);
for(int i=fir[x];i;i=next[i])
{
if(to[i]!=zson[x]&&to[i]!=fa[x]/*!!!of great importance*/) getpos(to[i],to[i]);
}
}
void build(int ll,int rr,int no)
{
int mid=(ll+rr)/2;
l[no]=ll;r[no]=rr;
if(ll==rr){qsum[no]=qmax[no]=val[fid[ll]];return;}
int le=no*2,ri=le+1;
build(ll,mid,le);build(mid+1,rr,ri);
qsum[no]=qsum[le]+qsum[ri];qmax[no]=max(qmax[le],qmax[ri]);
return;
}
int getmax(int ll,int rr,int no)
{
int le=no*2,ri=le+1;
int mid=(l[no]+r[no])/2;
if(ll==l[no]&&rr==r[no]) return qmax[no];
if(rr<=mid) return getmax(ll,rr,no*2);
if(ll>mid) return getmax(ll,rr,no*2+1);
return max(getmax(ll,mid,le),getmax(mid+1,rr,ri));
}
int getsum(int ll,int rr,int no)
{
int le=no*2,ri=le+1;
int mid=(l[no]+r[no])/2;
if(ll==l[no]&&rr==r[no]) return qsum[no];
if(rr<=mid) return getsum(ll,rr,no*2);
if(ll>mid) return getsum(ll,rr,no*2+1);
return getsum(ll,mid,le)+getsum(mid+1,rr,ri);
}
void change(int u,int t,int no)
{
int mid=(l[no]+r[no])/2;
if(l[no]==u&&r[no]==u) {qmax[no]=t;qsum[no]=t;return;}
if(mid>=u) 
change(u,t,no*2);else change(u,t,no*2+1);
int le=no*2,ri=le+1;
qsum[no]=qsum[le]+qsum[ri];
qmax[no]=max(qmax[le],qmax[ri]);
return;
}


void init()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++) 
{
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
scanf("%d",&q);
dfs(0,1,1);
getpos(1,1);build(1,pos,1);
}
char type[101];
int query_max(int u,int v)
{
int f1=top[u],f2=top[v];
int ans=-100000000;
while(f1!=f2)
{
if(depth[f1]<depth[f2])
{
swap(f1,f2);
swap(u,v);
}
ans=max(ans,getmax(id[f1],id[u],1));
u=fa[f1];f1=top[u];
}
if(depth[u]<depth[v])
{
swap(f1,f2);
swap(u,v);
}
return ans=max(ans,getmax(id[v],id[u],1));
}
int query_sum(int u,int v)
{
int f1=top[u],f2=top[v];
int ans=0;
while(f1!=f2)
{
if(depth[f1]<depth[f2])
{
swap(f1,f2);
swap(u,v);
}
ans+=getsum(id[f1],id[u],1);
u=fa[f1];f1=top[u];
}
if(depth[u]<depth[v])
{
swap(f1,f2);
swap(u,v);
}
return ans+=getsum(id[v],id[u],1);
}
void do_one_move()
{
scanf("%s%d%d",type,&x,&y);
if(strcmp(type,"QMAX") == 0){printf("%d\n",query_max(x,y));}
else if(strcmp(type,"QSUM")==0){printf("%d\n",query_sum(x,y));}
else{
change(id[x],y,1);
val[x]=y;
}
}
int main()
{
init();
while(q--) do_one_move();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值