HDU-6547Tree (树链剖分+线段树 区间开根号 区间求和)

it2022-05-05  154

Tree

wls 有三棵树,树上每个节点都有一个值 aiai,现在有 2 种操作:  1. 将一条链上的所有节点的值开根号向下取整;  2. 求一条链上值的和;  链的定义是两点之间的最短路。

Input

第一行两个数 nn, qq 分别代表树上点的数量和操作数量。  第二行 nn 个整数,第 ii 个数代表第 ii 个点的值 aiai。  接下来 nn −− 11 行, 每行两个整数 uu, vv 代表 uu,vv 之间有一条边。数据保证点两两联通。  接下来 qq 行,每行有个整数 opop, uu, vv,opop = 0 表示将 uu, vv 这条链上所有的点的值开根号向下取整,opop = 1表示询问 uu,vv 这条链上的值的和。  1 ≤ nn, qq ≤ 100, 000  0 ≤ aiai ≤ 1, 000, 000, 000

Output

对于每一组 opop = 2 的询问,输出一行一个值表示答案。

Sample Input

4 4 2 3 4 5 1 2 2 3 2 4 0 3 4 0 1 3 1 2 3 1 1 4

Sample Output

2 4

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6547

思路:树链剖分把树分成多条链,线段树维护区间和,实现区间开根号 区间求和

代码:

#include<stdio.h> #include<string.h> #include<math.h> #include<algorithm> using namespace std; #define mem(a,b) memset(a,b,sizeof(a)) #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define ll long long const int N=100005; int n,m,tot,cnt; int first[N]; ll sum[N<<2],w[N],wt[N]; int d[N],fa[N],siz[N],son[N],top[N],id[N],rk[N]; struct node { int v,nex; }e[N<<1]; void adde(int u,int v) { e[tot].v=v; e[tot].nex=first[u]; first[u]=tot++; } void init() { tot=cnt=0; memset(first,-1,sizeof(first)); memset(son,0,sizeof(son)); } void dfs1(int u,int pre,int depth) //处理 d,fa,siz,son 数组 { d[u]=depth; fa[u]=pre; siz[u]=1; for(int i=first[u];~i;i=e[i].nex) { int v=e[i].v; if(v==pre) continue; dfs1(v,u,depth+1); siz[u]+=siz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } void dfs2(int u,int t) //处理 top id rk wt 数组 { top[u]=t; id[u]=++cnt; rk[cnt]=u; wt[cnt]=w[u]; if(!son[u]) return; dfs2(son[u],t); for(int i=first[u];~i;i=e[i].nex) { int v=e[i].v; if(v!=son[u]&&v!=fa[u]) dfs2(v,v); } } void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt) //建线段树 { if(l==r) { sum[rt]=wt[l]; return; } int mid=(l+r)>>1; build(lson); build(rson); pushup(rt); } void update(int L,int R,int l,int r,int rt) //[L,R]区间开根号 { if(sum[rt]==r-l+1) return; //区间内所有点都为1,就不用再往下更新了 if(l==r) { sum[rt]=(ll)sqrt(sum[rt]); return; } int mid=(l+r)>>1; if(L<=mid) update(L,R,lson); if(mid<R) update(L,R,rson); pushup(rt); } ll query(int L,int R,int l,int r,int rt) // 查询[L,R]区间的和 { if(L<=l&&r<=R) return sum[rt]; int mid=(l+r)>>1; ll ans=0; if(L<=mid) ans+=query(L,R,lson); if(mid<R) ans+=query(L,R,rson); return ans; } void uprange(int x,int y) //x到y的最短路径开根 { while(top[x]!=top[y]) { if(d[top[x]]<d[top[y]]) swap(x,y); update(id[top[x]],id[x],1,n,1); x=fa[top[x]]; } if(d[x]>d[y]) swap(x,y); update(id[x],id[y],1,n,1); } ll querange(int x,int y) //求x到y的最短路径的和 { ll ans=0; while(top[x]!=top[y]) { if(d[top[x]]<d[top[y]]) swap(x,y); ans+=query(id[top[x]],id[x],1,n,1); x=fa[top[x]]; } if(d[x]>d[y]) swap(x,y); ans+=query(id[x],id[y],1,n,1); return ans; } int main() { scanf("%d%d",&n,&m); init(); for(int i=1;i<=n;i++) scanf("%lld",&w[i]); int u,v; for(int i=1;i<n;i++) //存原树 { scanf("%d%d",&u,&v); adde(u,v); adde(v,u); } dfs1(1,0,1); dfs2(1,1); build(1,n,1); int op,x,y; while(m--) { scanf("%d%d%d",&op,&x,&y); if(op==0) uprange(x,y); else if(op==1) printf("%lld\n",querange(x,y)); } return 0; }

 


最新回复(0)