ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang
Posted 6262369sss
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang相关的知识,希望对你有一定的参考价值。
题目链接:https://nanti.jisuanke.com/t/31451
题意:给出一棵有n个点的树,每个点的初始值都是0,m次查询,如果op==1,那么后面接两个数字,表示把第a层的点的值增加b,如果op==2,那么后面接一个数a,表示查询以点a为根节点的这颗子树的所有点的值的和,输出这个值。
思路:因为n,m都可能达到1e5,因为要查找一棵树所有点的和,并且还要修改,如果用树结构查找肯定不方便,那么我们可以用DFS序把树转化为线性结构,用每个点的DSF序来表示这个点,那么查找的时候一棵子树就是一个连续区间,那么我们就可以用树状数组来查找一段区间的和,如果只用树状数组的单点更新,那么极端情况下某一层的点数非常多,更新就会超时;为了解决某一层点非常多并且要更新的情况,我们就可以使用分块的思想来减少更新所需的时间。把所有的层分成大块(点非常多)和小块(点比较少),如果是小块,我们就用树状数组来处理它,更新时暴力更新这一层所有点。如果是大块,我们就用标记数组来记录该层增加的值,因为我们用树的DFS序来代表点,并且DFS序是升序的的,我们就可以用二分查找每个大块来计算目标子树的和。
#include<iostream> #include<cstring> #include<algorithm> #include<queue> #include<map> #include<stack> #include<cmath> #include<vector> #include<set> #include<cstdio> #include<string> #include<deque> using namespace std; typedef long long LL; #define eps 1e-8 #define INF 0x3f3f3f3f #define maxn 100005 /*struct point{ int u,w; }; bool operator <(const point &s1,const point &s2) { if(s1.w!=s2.w) return s1.w>s2.w; else return s1.u>s2.u; }*/ struct node{ int v,next; }edge[maxn*2]; int head[maxn],L[maxn],R[maxn],pre[maxn];//L[i]和R[i]数组表示i点DFS序的左右边界,pre[i]数组记录i的父亲,这里用来判断有没有走过 LL c[maxn],up[maxn];//c数组就是树状数组,up数组用来记录点数很多的层数(大块)的增加值 int n,m,k,r,cnt,Time,max_deep,block;//Time就是DFS序,max_deep记录最大的层数,block用来分块 vector<int>ve[maxn];//ve[deep]记录在第deep层的点 vector<int>vv; //vv存大块所在的层 void init() { fill(head,head+maxn-1,-1); fill(pre,pre+maxn-1,0); fill(c,c+maxn-1,0); fill(up,up+maxn-1,0); for(int i=0;i<=n;i++) ve[i].clear(); vv.clear(); Time=cnt=max_deep=0; block=sqrt(n); } void DFS(int u,int deep) { L[u]=++Time;//记录左边界 max_deep=max(max_deep,deep); ve[deep].push_back(Time);//把u点的DFS序值存入第deep层,我们用DFS序把把树转化为线性,然后用树状数组求区间和 for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; if(pre[v]) continue; pre[v]=u; DFS(v,deep+1); } R[u]=Time;//存右边界,注意Time不要加,因为一个点不用记录多个DFS序 } void add(int u,int v) { edge[++cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt; } int lowbit(int x) { return x&(-x); } void Add(int x,int b)//树状数组单点更新 { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=b; } LL sum(int x)//树状数组求区间和 { if(x<=0) return 0; LL ans=0; while(x>0) { ans+=c[x]; x-=lowbit(x); } return ans; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } pre[1]=-1; DFS(1,0);//u,depth for(int i=0;i<=max_deep;i++)//把大块存进vv数组 { if(ve[i].size()>block) vv.push_back(i); } int op; for(int i=1;i<=m;i++) { scanf("%d",&op); if(op==1) { int a,b; scanf("%d%d",&a,&b); if(ve[a].size()>block)//如果a层是大块 { up[a]+=b; } else//a层不是大块 { for(int i=0;i<ve[a].size();i++)//暴力更新每个点 Add(ve[a][i],b); } } else { int a; scanf("%d",&a); int l=L[a],r=R[a]; LL ans=sum(r)-sum(l-1);//我们在树状数组里存的是DFS序,求和是根据DFS序来求和 for(int i=0;i<vv.size();i++)//遍历所有大块来寻找属于子树的值 { int idx=vv[i]; ans+=(upper_bound(ve[idx].begin(),ve[idx].end(),r)-lower_bound(ve[idx].begin(),ve[idx].end(),l))*up[idx]; } printf("%lld ",ans); } } } return 0; }
以上是关于ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang的主要内容,如果未能解决你的问题,请参考以下文章
ACM-ICPC 2018 沈阳赛区网络预赛 F. Fantastic Graph
ACM-ICPC 2018 沈阳赛区网络预赛 K. Supreme Number