@Chilling
2017-02-18T03:18:13.000000Z
字数 3881
阅读 1308
树链剖分 线段树
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
部分摘自starszys的博客,但是这个博客上面的题是边权,因此以下内容有一定的修改……
树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。
重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
轻儿子:v的其它子节点。
重边:点v与其重儿子的连边。
轻边:点v与其轻儿子的连边。
重链:由重边连成的路径。
轻链:轻边。
剖分后的树有如下性质:
性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v];
性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。
分析:对于这道题来说,我们应该求出如下内容:
siz[u]表示以u为根的子树的节点数;
dep[u]表示u的深度(根深度为1);
fa[u]表示u的父亲,son[u]表示与u在同一重链上的u的儿子节点(重儿子);
top[u]表示u所在的重链的顶端节点;
pre[u]表示u的新编号。dfs1:求出siz[u],dep[u],fa[u],son[u];
dfs2:对于某点u,若它不是叶子节点,显然有top[son[u]] = top[u];节点u重新编号为pre[u]之后,valnow[pre[u]]=val[u];此时,为了使一条重链各边在线段树中连续分布,应当进行dfs2(son[u],u,id)。(id为重链顶端节点)
而对于u个各个轻儿子,top[v]=v。询问:
- 询问(u,v)路径上的最值,首先需要判断u和v是否在同一条重链上,若在同一条重链上,使u深v浅,查询(pre[v],pre[u])区间内的最值即可;
- 若u,v不在同一条重链上,那么判断u和v重链顶端的深度,使top[u]更深,先查询pre[top[u]]到pre[u],那么u点所在的这部分重链就已经查询完毕了;接着u等于fa[top[u]],继续以上操作,直到u和v在同一条重链上。
- 查询部分即线段树。
#include<stdio.h>#include<vector>#include<algorithm>#include<string.h>#define INF 0x3f3f3f3fconst int maxn=30005;using namespace std;int n;int val[maxn];int siz[maxn],dep[maxn],fa[maxn],son[maxn];//dfs1int pre[maxn];//对于节点v,pre[v]对应新编号++clkint valnow[maxn];//节点新编号后对应的值 valnow[clk]=val[v]int top[maxn];//v节点所在重链顶端的点int clk;struct node{int l,r,maxs,sum;}s[maxn*4];vector<int> V[maxn];void dfs1(int u,int f,int d)//维护fa,dep,siz,son{dep[u]=d; //深度fa[u]=f; //u的父亲siz[u]=1; //以u为根的子树节点//son[u]=0; //u的重儿子for(int i=0;i<V[u].size();i++){int v=V[u][i];if(v==f) continue;dfs1(v,u,d+1);if(siz[v]>siz[son[u]]) //更新重儿子son[u]=v;siz[u]+=siz[v];}}void dfs2(int u,int f,int id) //id为u所在的顶端节点{top[u]=id;pre[u]=++clk;valnow[clk]=val[u];if(!son[u]) return; //叶子节点,无重儿子dfs2(son[u],u,id);for(int i=0;i<V[u].size();i++){int v=V[u][i];if(v==f||v==son[u]) continue;dfs2(v,u,v);}}void build(int id,int l,int r){s[id].l=l;s[id].r=r;if(l==r)s[id].maxs=s[id].sum=valnow[l];else{int mid=(l+r)/2;build(id*2,l,mid);build(id*2+1,mid+1,r);s[id].maxs=max(s[id*2].maxs,s[id*2+1].maxs);s[id].sum=s[id*2].sum+s[id*2+1].sum;}}void rep(int id,int x,int num){if(s[id].l==s[id].r)s[id].maxs=s[id].sum=num;else{int mid=(s[id].l+s[id].r)/2;if(x<=mid)rep(id*2,x,num);elserep(id*2+1,x,num);s[id].maxs=max(s[id*2].maxs,s[id*2+1].maxs);s[id].sum=s[id*2].sum+s[id*2+1].sum;}}int qmax(int id,int l,int r){if(l<=s[id].l&&s[id].r<=r)return s[id].maxs;int mid=(s[id].l+s[id].r)/2;if(r<=mid)return qmax(id*2,l,r);else if(l>mid)return qmax(id*2+1,l,r);elsereturn max(qmax(id*2,l,r),qmax(id*2+1,l,r));}int qsum(int id,int l,int r){if(l<=s[id].l&&s[id].r<=r)return s[id].sum;int mid=(s[id].l+s[id].r)/2;if(r<=mid)return qsum(id*2,l,r);else if(l>mid)return qsum(id*2+1,l,r);elsereturn qsum(id*2,l,r)+qsum(id*2+1,l,r);}int ansmax(int u,int v){int ans=-INF;while(top[u]!=top[v])//不在一条重链上{if(dep[top[u]]<dep[top[v]])swap(u,v); //u重链顶端更深ans=max(ans,qmax(1,pre[top[u]],pre[u]));u=fa[top[u]];}if(dep[u]<dep[v])swap(u,v);ans=max(ans,qmax(1,pre[v],pre[u]));return ans;}int anssum(int u,int v){int ans=0;while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v); //u重链顶端更深ans+=qsum(1,pre[top[u]],pre[u]);u=fa[top[u]];}if(dep[u]<dep[v])swap(u,v);ans+=qsum(1,pre[v],pre[u]);return ans;}int main(){int x,y,q;char ch[9];scanf("%d",&n);for(int i=1;i<n;i++){scanf("%d%d",&x,&y);V[x].push_back(y);V[y].push_back(x);}for(int i=1;i<=n;i++)scanf("%d",&val[i]);dfs1(1,0,1);dfs2(1,0,1);build(1,1,n);scanf("%d",&q);while(q--){scanf("%s%d%d",ch,&x,&y);if(ch[3]=='N')rep(1,pre[x],y);else if(ch[3]=='X')printf("%d\n",ansmax(x,y));elseprintf("%d\n",anssum(x,y));}return 0;}