題目:http://acm.hdu.edu.cn/showproblem.php?pid=3966
題意:
給一棵樹,并給定各個點權的值,然后有3種操作:
I C1 C2 K: 把C1與C2的路徑上的所有點權值加上K
D C1 C2 K:把C1與C2的路徑上的所有點權值減去K
Q C:查詢節點編號為C的權值
分析:
樹鏈剖分入門題,基于點的重編號。
操作是區間增減+單點查詢
用樹狀數組或者線段樹維護操作
代碼:
樹狀數組
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <cmath>
#include <queue>
#include <set>
using namespace std;
const int N =
50010;
struct EDGE {
int v,nex;
} edge[N<<
1];
int head[N],tot;
int dep[N],p[N],fp[N],fa[N],top[N],son[N],siz[N];
int cnt;
int num[N];
void addedge(
int a,
int b) {edge[tot].v=b;edge[tot].nex=head[a];head[a]=tot++;
}
void dfs(
int u) {siz[u]=
1,son[u]=
0;
for(
int i=head[u]; ~i; i=edge[i].nex) {
int v=edge[i].v;
if(v!=fa[u]) {fa[v]=u;dep[v]=dep[u]+
1;dfs(v);
if(siz[v]>siz[son[u]])son[u]=v;siz[u]+=siz[v];}}
}
void build(
int u,
int tp) {p[u]=++cnt;fp[p[u]]=u;top[u]=tp;
if(son[u])build(son[u],tp);
for(
int i=head[u]; ~i; i=edge[i].nex) {
int v=edge[i].v;
if(v!=son[u]&&v!=fa[u])build(v,v);}
}
int arr[N];
inline int lowbit(
int x){
return x&(-x);}
inline int sum(
int x) {
int res=
0;
while(x)res+=arr[x],x-=lowbit(x);
return res;
}
inline void add(
int x,
int n) {
while(x<N)arr[x]+=n,x+=lowbit(x);
}
inline int update(
int x,
int y,
int n) {add(x,n);add(y+
1,-n);
}
void change(
int a,
int b,
int x) {
int f1=top[a],f2=top[b],tmp=
0;
while(f1!=f2) {
if(dep[f1]<dep[f2])swap(f1,f2),swap(a,b);update(p[f1],p[a],x);a=fa[f1],f1=top[a];}
if(dep[a]>dep[b])swap(a,b);update(p[a],p[b],x);
}
int n,m,q,a,b,c;
char op[
10];
int main() {
int T;
while(~
scanf(
"%d%d%d",&n,&m,&q)) {
memset(head,-
1,
sizeof(head));
memset(arr,
0,
sizeof(arr));cnt=tot=
0;
for(
int i=
1; i<=n; i++)
scanf(
"%d",&num[i]);
for(
int i=
0; i<m; i++) {
scanf(
"%d%d",&a,&b);addedge(a,b);addedge(b,a);}dfs(
1);build(
1,
1);
for(
int i=
1;i<=n;i++)update(p[i],p[i],num[i]);
while(q--) {
scanf(
"%s",op);
if(op[
0]==
'Q') {
scanf(
"%d",&a);
printf(
"%d\n",sum(p[a]));}
else {
scanf(
"%d%d%d",&a,&b,&c);
if(op[
0]==
'D')c=-c;change(a,b,c);}}}
return 0;
}
線段樹:
這題用線段樹不如樹狀數組簡單,但是學習下套路還是挺不錯的
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <cmath>
#include <queue>
#include <set>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int N =
50010;
struct EDGE {
int v,nex;
} edge[N<<
1];
int head[N],tot;
int dep[N],p[N],fp[N],fa[N],top[N],son[N],siz[N];
int cnt,sum[N<<
2],col[N<<
2];
int num[N];
void addedge(
int a,
int b) {edge[tot].v=b;edge[tot].nex=head[a];head[a]=tot++;
}
void dfs(
int u) {siz[u]=
1,son[u]=
0;
for(
int i=head[u]; ~i; i=edge[i].nex) {
int v=edge[i].v;
if(v!=fa[u]) {fa[v]=u;dep[v]=dep[u]+
1;dfs(v);
if(siz[v]>siz[son[u]])son[u]=v;siz[u]+=siz[v];}}
}
void build(
int u,
int tp) {p[u]=++cnt;fp[p[u]]=u;top[u]=tp;
if(son[u])build(son[u],tp);
for(
int i=head[u]; ~i; i=edge[i].nex) {
int v=edge[i].v;
if(v!=son[u]&&v!=fa[u])build(v,v);}
}
void pushDown(
int rt,
int m) {
if(col[rt]) {col[rt<<
1]+=col[rt];col[rt<<
1|
1]+=col[rt];sum[rt<<
1]+=(m-(m>>
1))*col[rt];sum[rt<<
1|
1]+=(m>>
1)*col[rt];col[rt]=
0;}
}
void build(
int l,
int r,
int rt) {col[rt]=
0;
if(l==r) {sum[rt]=num[fp[l]];
return;}
int m=l+r>>
1;build(lson);build(rson);
}
int query(
int p,
int l,
int r,
int rt) {
if(l==r) {
return sum[rt];}pushDown(rt,r-l+
1);
int m=l+r>>
1;
int ret=
0;
if(p<=m)ret=query(p,lson);
else ret=query(p,rson);
return ret;
}
void update(
int a,
int b,
int x,
int l,
int r,
int rt) {
if(a<=l&&r<=b) {col[rt]+=x;sum[rt]+=x*(r-l+
1);
return;}pushDown(rt,r-l+
1);
int m=l+r>>
1;
if(a<=m)update(a,b,x,lson);
if(b>m)update(a,b,x,rson);
}
void change(
int a,
int b,
int x) {
int f1=top[a],f2=top[b],tmp=
0;
while(f1!=f2) {
if(dep[f1]<dep[f2])swap(f1,f2),swap(a,b);update(p[f1],p[a],x,
1,cnt,
1);a=fa[f1],f1=top[a];}
if(dep[a]>dep[b])swap(a,b);update(p[a],p[b],x,
1,cnt,
1);
}
int n,m,q,a,b,c;
char op[
10];
int main() {
int T;
while(~
scanf(
"%d%d%d",&n,&m,&q)) {
memset(head,-
1,
sizeof(head));cnt=tot=
0;
for(
int i=
1; i<=n; i++)
scanf(
"%d",&num[i]);
for(
int i=
0; i<m; i++) {
scanf(
"%d%d",&a,&b);addedge(a,b);addedge(b,a);}dfs(
1);build(
1,
1);build(
1,n,
1);
while(q--) {
scanf(
"%s",op);
if(op[
0]==
'Q') {
scanf(
"%d",&a);
printf(
"%d\n",query(p[a],
1,n,
1));}
else {
scanf(
"%d%d%d",&a,&b,&c);
if(op[
0]==
'D')c=-c;change(a,b,c);}}}
return 0;
}
總結
以上是生活随笔為你收集整理的hdu 3966 (树链剖分,树状数组/线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。