HDU 3966 /// 树链剖分+树状数组
题意:
http://acm.hdu.edu.cn/showproblem.php?pid=3966
给一棵树,并给定各个点权的值,然后有3种操作:
I x y z : 把x到y的路径上的所有点权值加上z
D x y z:把x到y的路径上的所有点权值减去z
Q z:查询节点编号为x的权值
这里主要放下用树状数组维护的模板
区间修改单点查询 好像用线段树更好?
em.... 两种都放好了~
好像说hduoj是windows系统容易爆栈 手动扩栈加这句
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <bits/stdc++.h> using namespace std; #define mem(i,j) memset(i,j,sizeof(i)) #define LL long long #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 5e4 + 10; const int maxnode = maxn<<2; LL head[maxn], tot, pos; LL fa[maxn], son[maxn], dep[maxn], num[maxn]; // i的父亲、i的重结点、i的深度、i的儿子个数 LL top[maxn], p[maxn], fp[maxn]; // i所在链的顶端、ID->dfsID、dfsID->ID LL n,m,q; LL val[maxn]; struct Edge { int to,ne; }e[maxnode]; void init() { tot=1; pos=0; mem(head,0); mem(son,0); } void add(int u,int v) { e[tot].to = v; e[tot].ne = head[u]; head[u] = tot++; } struct IntervalTree { LL _sum, _min, _max; LL sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode]; void init() { mem(sumv,0); mem(setv,0); mem(addv,0); } void maintain(int L, int R, int rt) { int lc = rt<<1, rc = rt<<1|1; if(R > L) { sumv[rt] = sumv[lc] + sumv[rc]; minv[rt] = min(minv[lc], minv[rc]); maxv[rt] = max(maxv[lc], maxv[rc]); } if(setv[rt] >= 0) { minv[rt] = maxv[rt] = setv[rt]; sumv[rt] = setv[rt] * (R-L+1); } if(addv[rt]) { minv[rt] += addv[rt]; maxv[rt] += addv[rt]; sumv[rt] =sumv[rt]+addv[rt] * (R-L+1); } } void pushdown(int rt) { int lc = rt*2, rc = rt*2+1; if(setv[rt] >= 0) { setv[lc] = setv[rc] = setv[rt]; addv[lc] = addv[rc] = 0; setv[rt] = -1; } if(addv[rt]) { addv[lc] += addv[rt]; addv[rc] += addv[rt]; addv[rt] = 0; } } ///update(更新区间左右端点、更新值、更新选项 op=1为加减 op!=1为置值、当前区间左右端点、根) void update(int L, int R, LL v, int op, int l, int r, int rt){ //int lc = rt<<1, rc = rt<<1|1; if(L <= l && R >= r) { if(op == 1) addv[rt] += v; else { setv[rt] = v; addv[rt] = 0; } } else { pushdown(rt); int m = l + (r-l)/2; if(L <= m) update(L, R, v, op, lson); else maintain(lson); if(R > m) update(L, R, v, op, rson); else maintain(rson); } maintain(l, r, rt); } ///query(问询的左右端点、累加lazy_tag的累加量、当前区间左右端点、根) void query(int L, int R, LL add, int l, int r, int rt) { if(setv[rt] >= 0) { LL v = setv[rt] + add + addv[rt]; _sum += v * (LL)(min(r,R)-max(l,L)+1); _min = min(_min, v); _max = max(_max, v); } else if(L <= l && R >= r) { _sum += sumv[rt] + add * (LL)(r-l+1); _min = min(_min, minv[rt] + add); _max = max(_max, maxv[rt] + add); } else { int m = l + (r-l)/2; if(L <= m) query(L, R, add+addv[rt], lson); if(R > m) query(L, R, add+addv[rt], rson); } } }T; /** -----树链剖分----- */ void dfs1(int u,int pre,int d) { dep[u]=d; fa[u]=pre; num[u]=1; for(int i=head[u];i;i=e[i].ne) { int v=e[i].to; if(v!=fa[u]) { dfs1(v,u,d+1); num[u]+=num[v]; if(!son[u] || num[v]>num[son[u]]) son[u]=v; } } } void dfs2(int u,int sp) { top[u]=sp; p[u]=++pos; fp[p[u]]=u; if(!son[u]) return; dfs2(son[u],sp); for(int i=head[u];i;i=e[i].ne) { int v=e[i].to; if(v!=son[u] && v!=fa[u]) dfs2(v,v); } } // 查询树上x到y的总和 LL queryPath(int x,int y) { LL ans=0LL; int fx=top[x], fy=top[y]; // fx==fy 说明到了LCA while(fx!=fy) { // x y不在同一条重链上 if(dep[fx]>=dep[fy]) { T._sum=0LL; T.query(p[fx],p[x],0,1,pos,1); ans=(ans+T._sum)%mod; x=fa[fx]; } else { T._sum=0LL; T.query(p[fy],p[y],0,1,pos,1); ans=(ans+T._sum)%mod; y=fa[fy]; } // 先加离LCA更远的 且只加到父亲节点的一段 一步步移 fx=top[x], fy=top[y]; } // 直到两点在同一条重链上跳出 此时节点必连续 // 将最后到达LCA的一段连续的区间加上 if(p[x]>p[y]) swap(x,y); T._sum=0LL; T.query(p[x],p[y],0,1,n,1); return (ans+T._sum)%mod; } // 将树上x到y都加上z (和queryPath()差不多) void updatePath(LL x,LL y,LL z) { int fx=top[x], fy=top[y]; while(fx!=fy) { if(dep[fx]>=dep[fy]) { T.update(p[fx],p[x],z,1,1,n,1); x=fa[fx]; } else { T.update(p[fy],p[y],z,1,1,n,1); y=fa[fy]; } fx=top[x], fy=top[y]; } if(p[x]>p[y]) swap(x,y); T.update(p[x],p[y],z,1,1,n,1); } /** ---------------- */ int main() { while(~scanf("%lld%lld%lld",&n,&m,&q)) { init(); for(int i=1;i<=n;i++) scanf("%lld",&val[i]); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } dfs1(1,0,1); // 根节点 前驱节点 深度 dfs2(1,1); // 当前节点 起始重结点 T.init(); for(int i=1;i<=n;i++) T.update(p[i],p[i],val[fp[p[i]]],1,1,n,1); while(q--) { LL x,y,z; char op; scanf(" %c",&op); //printf("op%d\n",op); if(op=='Q') { scanf("%lld",&x); T._sum=0; T.query(p[x],p[x],0LL,1,n,1); printf("%lld\n",T._sum); } else { scanf("%lld%lld%lld",&x,&y,&z); if(op=='D') z=-z; updatePath(x,y,z); } } } return 0; } 线段树
#include <bits/stdc++.h> using namespace std; #define mem(i,j) memset(i,j,sizeof(i)) #define LL long long #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 1 const int maxn = 5e4 + 10; const int maxnode = maxn<<2; const int maxedge = maxn<<2; LL head[maxn], tot, pos; LL fa[maxn], son[maxn], dep[maxn], num[maxn]; // i的父亲、i的重结点、i的深度、i的儿子个数 LL top[maxn], p[maxn], fp[maxn]; // i所在链的顶端、ID->dfsID、dfsID->ID LL n,m,q; LL val[maxn]; struct Edge { int to,ne; }e[maxedge]; void init() { tot=1; mem(head,0); pos=0; mem(son,0); } void add(int u,int v) { e[tot].to = v; e[tot].ne = head[u]; head[u] = tot++; } struct Tree { int N; LL sumT[maxn]; void init() { N=1; while(N<=n) N<<=1; mem(sumT,0); } int lowbit(int i) { return -i&i; } void add(int i,LL x) { while(i<=N) { sumT[i]+=x; i+=lowbit(i); } } LL sum(int i) { LL res=0LL; while(i) { res+=sumT[i]; i-=lowbit(i); } return res; } }T; /** -----树链剖分----- */ void dfs1(int u,int pre,int d) { dep[u]=d; fa[u]=pre; num[u]=1; for(int i=head[u];i;i=e[i].ne) { int v=e[i].to; if(v!=fa[u]) { dfs1(v,u,d+1); num[u]+=num[v]; if(!son[u] || num[v]>num[son[u]]) son[u]=v; } } } void dfs2(int u,int sp) { top[u]=sp; p[u]=++pos; fp[p[u]]=u; if(!son[u]) return; dfs2(son[u],sp); for(int i=head[u];i;i=e[i].ne) { int v=e[i].to; if(v!=son[u] && v!=fa[u]) dfs2(v,v); } } // 将树上x到y都加上z (和queryPath()差不多) void updatePath(LL x,LL y,LL z) { int fx=top[x], fy=top[y]; while(fx!=fy) { if(dep[fx]>=dep[fy]) { T.add(p[fx],z); T.add(p[x]+1,-z); x=fa[fx]; } else { T.add(p[fy],z); T.add(p[y]+1,-z); y=fa[fy]; } fx=top[x], fy=top[y]; } if(p[x]>p[y]) swap(x,y); T.add(p[x],z); T.add(p[y]+1,-z); } /** ---------------- */ int main() { while(~scanf("%lld%lld%lld",&n,&m,&q)) { init(); for(int i=1;i<=n;i++) scanf("%lld",&val[i]); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } dfs1(1,0,1); // 根节点 前驱节点 深度 dfs2(1,1); // 当前节点 起始重结点 T.init(); for(int i=1;i<=n;i++) T.add(p[i],val[i]), T.add(p[i]+1,-val[i]); while(q--) { LL x,y,z; char op; scanf(" %c",&op); if(op=='Q') { scanf("%lld",&x); printf("%lld\n",T.sum(p[x])); } else { scanf("%lld%lld%lld",&x,&y,&z); if(op=='D') z=-z; updatePath(x,y,z); } } } return 0; }