题目传送门:[ZJOI2008]树的统计Count
题目大意:
一棵树上有n个节点,每个节点都有一个权值w。进行以下的一些操作
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和
分析:
树链剖分入门题,线段树维护点权最大值mmax和区间和ssum即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAX=30005; #define INF 1<<29 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 int head[MAX],cnt; int fa[MAX]; //fa[x] x点的父节点 int deep[MAX]; //deep[x] 记录树中x点的深度 int size[MAX]; //size[x] 记录x节点的子树节点个数 int son[MAX]; //son[x] 记录x节点的重儿子 int top[MAX]; //top[x] 记录x所在重链的顶端节点 int id[MAX]; //id[x] 记录x节点在线段树中的位置 int rk[MAX],tot; //rk[x] 记录在线段树x位置的节点是什么节点,与id[x]相反 int n,x,y,a[MAX],q,u,v; char op[10]; struct Tree{ int mmax,ssum; //最大值和区间和 }tree[MAX*4]; struct Edge{ int next,to; }edge[MAX*2]; inline void add(int u,int v) { edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } inline int max(int a,int b) { return a>b?a:b; } void dfs1(int u,int f,int d) //dfs1求出deep,fa,size,son数组 { deep[u]=d; fa[u]=f; size[u]=1; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(v!=fa[u]) { dfs1(v,u,d+1); size[u]+=size[v]; if(son[u]==-1||size[v]>size[son[u]]) son[u]=v; } } } void dfstop(int u,int t) //dfstop 划分重链 { top[u]=t; id[u]=tot++; rk[id[u]]=u; if(son[u]==-1)return; dfstop(son[u],t); for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(v!=son[u]&&v!=fa[u]) dfstop(v,v); } } /*线段树部分*/ void PushUp(int rt) { tree[rt].mmax=max(tree[rt<<1].mmax,tree[rt<<1|1].mmax); tree[rt].ssum=tree[rt<<1].ssum+tree[rt<<1|1].ssum; } void Build(int l,int r,int rt) { if(l==r) { tree[rt].ssum=a[rk[l]]; tree[rt].mmax=a[rk[l]]; return; } int m=l+r>>1; Build(ls),Build(rs); PushUp(rt); } void Update(int pos,int val,int l,int r,int rt) //单点更新,将pos位置点值更新为val { if(l==r) { tree[rt].mmax=val; tree[rt].ssum=val; return; } int m=l+r>>1; if(pos<=m)Update(pos,val,ls); else Update(pos,val,rs); PushUp(rt); } int QMAX(int L,int R,int l,int r,int rt) //查询[l,r]区间最大值 { if(L<=l&&r<=R) return tree[rt].mmax; int m=l+r>>1; int ans=-INF; if(L<=m)ans=max(ans,QMAX(L,R,ls)); if(R>m)ans=max(ans,QMAX(L,R,rs)); return ans; } int QSUM(int L,int R,int l,int r,int rt) //查询[l,r]区间和 { if(L<=l&&r<=R) return tree[rt].ssum; int m=l+r>>1; int ans=0; if(L<=m)ans+=QSUM(L,R,ls); if(R>m)ans+=QSUM(L,R,rs); return ans; } int solveMax(int x,int y) //查询x->y路径节点最大值 { int res=-INF; while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]]) swap(x,y); res=max(res,QMAX(id[top[x]],id[x],1,tot,1)); x=fa[top[x]]; } if(id[x]>id[y])swap(x,y); res=max(res,QMAX(id[x],id[y],1,tot,1)); return res; } int solveSum(int x,int y) //查询x->y路径节点的权值和 { int res=0; while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]]) swap(x,y); res+=QSUM(id[top[x]],id[x],1,tot,1); x=fa[top[x]]; } if(id[x]>id[y])swap(x,y); res+=QSUM(id[x],id[y],1,tot,1); return res; } void init() { memset(head,-1,sizeof(head));cnt=0; memset(son,-1,sizeof(son));tot=1; memset(a,0,sizeof(a)); memset(tree,0,sizeof(tree)); } int main() { init(); scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); add(u,v);add(v,u); } for(int i=1;i<=n;i++) scanf("%d",&a[i]); dfs1(1,-1,0);dfstop(1,1); Build(1,tot,1); scanf("%d",&q); while(q--) { scanf("%s",op); scanf("%d%d",&x,&y); if(op[1]=='M')printf("%d ",solveMax(x,y)); if(op[1]=='S')printf("%d ",solveSum(x,y)); if(op[1]=='H')Update(id[x],y,1,tot,1); } return 0; }
总结:很普通的树链剖分裸题,但是写的时候莫名其妙挂掉..一直无法通过,最后莫名其妙又过了,很奇怪QAQ