【2018沈阳赛区网络预选赛J题】Ka Chang【分块+DFS序+线段树】
题意
给出一个有根树(根是1),有n个结点。初始的时候每个结点的值都是0.下面有q个操作,操作有两种,操作1.将深度为L的点的值全部增加X。操作2.查询以x为根的子树的结点值得和。
其中N,Q<=1e5
分析
一看这种没有办法直接用数据结构解决得问题就要考虑分块。这个题其实也不算是分块,应该是用了分块的思想进行分类而已。场上也一直在想分块但是可能是自己太菜了,赛后看了题解补的。
分块最重要的就是算时间复杂度啊。我们按照每一层的结点数进行分类。节点数>block的为第一类,节点数<=为第二类。
对于第二类,每次修改操作我们暴力修改每个结点的影响值,因为涉及线段树或者树状数组的操作,时间复杂度为O(q*block*logn)。而每次通过线段树查询都是logn的
对于第一类,当修改的时候直接记录这一层被增加了多少,O(1)修改,然后查询的时候只需要枚举第二类的每一层,然后以这个结点为根节点的子树中属于这一层的节点数*这一层增加的值。这里的时间复杂度是O(q*n/block)
我们需要预处理出每个结点的子树中属于第一类层的节点数各有多少。这里我用的办法就是直接暴力。枚举每个点,如果它所在的层是第一类,那么更新它所有的父节点。这里的时间复杂度很容易被认为是O(n^2)(所以我一直不打敢写)。但是我们仔细分析一下发现它远远小于O(n^2)。因为最多有n/block层,所以这里的时间复杂度是O(n*n/block)
先不考虑预处理,只看操作的时间复杂度是O(q*block*logn+q*n/block).根据均值不等式最小是O(q*2*sqrt(nlogn)),当且仅当block取sqrt(n/logn)。这时候预处理的时间复杂度是O(n*sqrt(n*logn))经过计算时可以承受的(因为只有单组数据)。
这种题目时间复杂度计算明白以后写起来还是很好写的
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 #include <queue> 6 #include <cmath> 7 #include <map> 8 #include <vector> 9 10 using namespace std; 11 typedef long long LL; 12 const int maxn=100000+100; 13 int head[maxn],to[maxn*2],Next[2*maxn],Fa[maxn]; 14 int n,q,sz,block,maxd; 15 int order[maxn],R[maxn],num,belong[maxn],L[maxn],pos[maxn]; 16 17 map<int,int>M[maxn]; 18 LL addv[maxn],sumv[4*maxn]; 19 vector<int>deep[maxn]; 20 void init(){ 21 sz=-1; 22 maxd=0; 23 num=0; 24 // for(int i=0;i<=n;i++)M[i].clear(); 25 // for(int i=0;i<=n;i++)deep[i].clear(); 26 memset(head,-1,sizeof(head)); 27 // memset(addv,0,sizeof(addv)); 28 } 29 void add_edge(int a,int b){ 30 ++sz; 31 to[sz]=b;Next[sz]=head[a];head[a]=sz; 32 } 33 34 void dfs(int u,int fa,int dep){ 35 maxd=max(maxd,dep); 36 Fa[u]=fa; 37 num++; 38 order[num]=u;L[u]=num;belong[u]=dep;pos[u]=num; 39 deep[dep].push_back(u); 40 for(int i=head[u];i!=-1;i=Next[i]){ 41 int v=to[i]; 42 if(v==fa)continue; 43 dfs(v,u,dep+1); 44 } 45 R[u]=num; 46 } 47 int p,v; 48 void update(int o,int L,int R){ 49 if(L==R){ 50 sumv[o]+=v; 51 return ; 52 } 53 int M=L+(R-L)/2; 54 if(p<=M) 55 update(2*o,L,M); 56 if(p>M) 57 update(2*o+1,M+1,R); 58 sumv[o]=sumv[2*o]+sumv[2*o+1]; 59 } 60 int ql,qr; 61 LL res; 62 void query(int o,int L,int R){ 63 if(ql<=L&&qr>=R){ 64 res+=sumv[o]; 65 return ; 66 } 67 int M=L+(R-L)/2; 68 if(ql<=M) 69 query(2*o,L,M); 70 if(qr>M) 71 query(2*o+1,M+1,R); 72 } 73 LL ask(int root){ 74 LL res=0; 75 map<int,int>::iterator it; 76 for(it=M[root].begin();it!=M[root].end();it++){ 77 res+=(LL)it->second*addv[it->first]; 78 } 79 return res; 80 } 81 int main(){ 82 scanf("%d%d",&n,&q); 83 init(); 84 int a,b,c; 85 for(int i=1;i<n;i++){ 86 scanf("%d%d",&a,&b); 87 add_edge(a,b); 88 } 89 dfs(1,-1,0); 90 block=sqrt(n/log(n)); 91 num=0; 92 for(int i=1;i<=n;i++){ 93 if(deep[belong[i]].size()>block){ 94 int u=i; 95 while(u!=-1){ 96 if(!M[u].count(belong[i])) 97 M[u][belong[i]]=1; 98 else 99 M[u][belong[i]]++; 100 u=Fa[u]; 101 } 102 } 103 } 104 for(int i=1;i<=q;i++){ 105 scanf("%d",&a); 106 if(a==1){ 107 scanf("%d%d",&b,&c); 108 if(deep[b].size()>block){ 109 addv[b]+=c; 110 }else{ 111 for(int j=0;j<deep[b].size();j++){ 112 int u=deep[b][j]; 113 p=pos[u],v=c; 114 update(1,1,n); 115 } 116 } 117 }else{ 118 scanf("%d",&b); 119 res=0; 120 ql=L[b],qr=R[b]; 121 query(1,1,n); 122 LL ans=res+ask(b); 123 printf("%lld\n",ans); 124 } 125 } 126 127 return 0; 128 }