Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=3966
【前言】
前几天在学树链剖分。主要还是为比赛做一下模板。但是找不到很基础的题可以做。
这道题的线段树部分不是最简单的那种,用的也是别人的模板。
上次WA了,好几天还没碰。
但是今天早上起来看了一下,就改了一个地方,然后就过了。
看来应了那句话:把最困难的任务放在早上做。
【思路】
简单说下树链剖分。
树链剖分的应用就是动态地修改一棵树上的边权,同时动态地查询每两个节点之间路径上的信息,包括最值、和等。
树链剖分就是把一棵树从上到下划分为一条条链,这样做的好处是更新或查询的时候可以成段地进行。
详细请看:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html
网上代码很多,讲解的很少。上面这篇文章我觉得里面讲的很详细,例子给的也很到位。
当看懂之后发现,更新或查询的时候就是使用线段树了!
【代码】
树链剖分和线段树代码都是用的别人的,不过其实树链剖分没有想象中的那么困难。
#include <iostream> using namespace std; const int maxn = 50010; struct edgeNode { int value; int to; int next; }edge[maxn*2]; int ect; int ehead[maxn]; int eroot; int tct; int dep[maxn],w[maxn],fa[maxn],top[maxn],son[maxn],siz[maxn]; void initEdge(int n)//树的初始化 { ect = 0; eroot = 1; int i; for (i=0; i<=n; i++) ehead[i] = -1; } void insertEdge(int from,int to,int value)//插入一条树的边 { edge[ect].to = to; edge[ect].value = value; edge[ect].next = ehead[from]; ehead[from] = ect; ect++; } void dfs1(int v)//第一层搜索 { siz[v] = 1; son[v] = 0; int i; for (i=ehead[v]; i+1!=0; i=edge[i].next) { if (edge[i].to!=fa[v]) { fa[edge[i].to] = v; dep[edge[i].to] = dep[v]+1; dfs1(edge[i].to); if (siz[edge[i].to]>siz[son[v]]) son[v]=edge[i].to; siz[v] += siz[edge[i].to]; } } } void dfs2(int v,int tp)//第二层搜索,获得每个节点在线段树中的位置 { tct++; w[v] = tct; top[v] = tp; if (son[v]!=0) dfs2(son[v],top[v]); int i; for (i=ehead[v]; i+1!=0; i=edge[i].next) { if (edge[i].to!=son[v] && edge[i].to!=fa[v]) dfs2(edge[i].to,edge[i].to); } } int enemy[maxn]; //以下为线段树部分 #define lson l,m,rt << 1 #define rson m + 1,r,rt << 1 | 1 #define LL int LL add[maxn<<2]; LL sum[maxn<<2]; void PushUp(int rt) { sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } void PushDown(int rt,int m) { if (add[rt]) { add[rt<<1] += add[rt]; add[rt<<1|1] += add[rt]; sum[rt<<1] += add[rt] * (m - (m >> 1)); sum[rt<<1|1] += add[rt] * (m >> 1); add[rt] = 0; } } void buildSegment(int l,int r,int rt) //建立线段树,可以直接全部初始化为0 { memset(add,sizeof(add)); memset(sum,sizeof(sum)); /* add[rt] = 0; if (l==r) { sum[rt] = 0; return; } int m = (l + r) >> 1; buildSegment(lson); buildSegment(rson); PushUp(rt);*/ } void updateSegment(int L,int R,int c,int l,int rt)//线段树区间更新 { if (L<=l && r<=R) { add[rt] += c; sum[rt] += (LL)c * (r - l + 1); return; } PushDown(rt,r - l + 1); int m = (l + r) >> 1; if (L<=m) updateSegment(L,R,c,lson); if (m<R) updateSegment(L,rson); PushUp(rt); } LL querySum(int L,int rt)//线段树区间求和,不过这个不是树链剖分的区间求和 { if (L <= l && r <= R) { return sum[rt]; } PushDown(rt,r - l + 1); int m = (l + r) >> 1; LL ret = 0; if (L<=m) ret += querySum(L,lson); if (m<R) ret += querySum(L,rson); return ret; } void init(int n)//所有初始化 { eroot = (n + 1) / 2; fa[eroot] = tct = dep[eroot] = 0; memset(siz,sizeof(siz)); dfs1(eroot); dfs2(eroot,eroot); buildSegment(1,tct,1);//建立空的线段树 int i; for (i=1; i<=n; i++)//把初始的权值更新到线段树上 { updateSegment(w[i],w[i],enemy[i],1,1); } } void update(int va,int vb,int c)//树链剖分的更新 { int f1 = top[va],f2 = top[vb]; while(f1!=f2) { if (dep[f1]<dep[f2]) { swap(f1,f2); swap(va,vb); } updateSegment(w[f1],w[va],1); va = fa[f1]; f1 = top[va]; } // if (va==vb) return;//因为这道题是以点位权的,所以点需要更新 if (dep[va]>dep[vb]) { swap(va,vb); } updateSegment(w[va],w[vb],1); } int main() { int n,p; int i; int x,y,z; char cmd[5]; while(scanf("%d %d %d",&n,&m,&p)!=EOF) { for (i=1; i<=n; i++) scanf("%d",&enemy[i]); initEdge(n); for (i=1; i<n; i++) { scanf("%d %d",&x,&y); insertEdge(x,0); insertEdge(y,x,0); } init(n); for (i=0; i<p; i++) { scanf("%s",cmd); if (cmd[0]=='I') { scanf("%d %d %d",&y,&z); update(x,z); } else if (cmd[0]=='D') { scanf("%d %d %d",-z); } else { scanf("%d",&x); printf("%d\n",querySum(w[x],w[x],1)); } } } return 0; }