【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count

很早之前用树链剖分写过,但是代码太长太难写,省选现场就写错了。

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 using namespace std;
  5 #define lson rt<<1,l,m
  6 #define rson rt<<1|1,m+1,r
  7 #define maxn 60000
  8 int n,m,u,v;
  9 int V[maxn],Next[maxn],First[maxn];
 10 int Val[maxn];
 11 int fa[maxn],dep[maxn],son[maxn],siz[maxn],top[maxn],Num[maxn],tot,en;
 12 int maxv[maxn<<2],sumv[maxn<<2];
 13 bool vis[maxn];
 14 char s[7];
 15 inline void AddEdge(int UU,int VV)
 16 {
 17     V[++en]=VV;
 18     Next[en]=First[UU];
 19     First[UU]=en;
 20 }
 21 int query_sum(int ql,int qr,int rt,int l,int r)
 22 {
 23     if(ql<=l&&r<=qr)
 24       return sumv[rt];
 25     int m=l+r>>1,ans=0;
 26     if(ql<=m)
 27       ans+=query_sum(ql,qr,lson);
 28     if(m<qr)
 29       ans+=query_sum(ql,qr,rson);
 30     return ans;
 31 }
 32 int query_max(int ql,int qr,int rt,int l,int r)
 33 {
 34     if(ql<=l&&r<=qr)
 35       return maxv[rt];
 36     int m=l+r>>1,ans=-2147483647;
 37     if(ql<=m)
 38       ans=max(ans,query_max(ql,qr,lson));
 39     if(m<qr)
 40       ans=max(ans,query_max(ql,qr,rson));
 41     return ans;
 42 }
 43 void update(int p,int v,int rt,int l,int r)
 44 {
 45     int m=l+r>>1;
 46     if(l==r)
 47       {
 48         maxv[rt]=sumv[rt]=v;
 49         return;
 50       }
 51     if(p<=m)
 52       update(p,v,lson);
 53     else
 54       update(p,v,rson);
 55     sumv[rt]=sumv[rt<<1]+sumv[(rt<<1)+1];
 56     maxv[rt]=max(maxv[rt<<1],maxv[(rt<<1)+1]);
 57 }
 58 inline void Change(int p,int v)
 59 {
 60     update(p,v,1,1,n);
 61 }
 62 inline int Query_sum(int u,int v)
 63 {
 64     int f1=top[u],f2=top[v],res=0;
 65     while(f1!=f2)
 66       {
 67         if(dep[f1]<dep[f2])
 68           {
 69             swap(u,v);
 70             swap(f1,f2);
 71           }
 72         res+=query_sum(Num[f1],Num[u],1,1,n);
 73         u=fa[f1];
 74         f1=top[u];
 75       }
 76     if(dep[u]>dep[v])
 77       swap(u,v);
 78     return res+query_sum(Num[u],Num[v],1,1,n);
 79 }
 80 inline int Query_max(int u,int v)
 81 {
 82     int f1=top[u],f2=top[v],res=-2147483647;
 83     while(f1!=f2)
 84       {
 85         if(dep[f1]<dep[f2])
 86           {
 87             swap(u,v);
 88             swap(f1,f2);
 89           }
 90         res=max(res,query_max(Num[f1],Num[u],1,1,n));
 91         u=fa[f1];
 92         f1=top[u];
 93       }
 94     if(dep[u]>dep[v])
 95       swap(u,v);
 96     return max(res,query_max(Num[u],Num[v],1,1,n));
 97 }
 98 void dfs1(int cur,int father,int depth)
 99 {
100     fa[cur]=father;
101     dep[cur]=depth;
102     siz[cur]=1;
103     for(int i=First[cur];i;i=Next[i])
104       if(!vis[V[i]])
105         {
106           vis[V[i]]=true;
107           dfs1(V[i],cur,depth+1);
108           siz[cur]+=siz[V[i]];
109           if(siz[V[i]]>siz[son[cur]])
110             son[cur]=V[i];
111           vis[V[i]]=false;
112         }
113 }
114 void dfs2(int cur)
115 {
116     if(son[cur]&&!vis[son[cur]])
117       {
118         vis[son[cur]]=true;
119         top[son[cur]]=top[cur];
120         Num[son[cur]]=++tot;
121         Change(tot,Val[son[cur]]);
122         dfs2(son[cur]);
123         vis[son[cur]]=false;
124       }
125     for(int i=First[cur];i;i=Next[i])
126       if(son[cur]!=V[i]&&!vis[V[i]])
127         {
128           vis[V[i]]=true;
129           top[V[i]]=V[i];
130           Num[V[i]]=++tot;
131           Change(tot,Val[V[i]]);
132           dfs2(V[i]);
133           vis[V[i]]=false;
134         }
135 }
136 int main()
137 {
138     scanf("%d",&n);
139     for(int i=1;i<n;i++)
140       {
141         scanf("%d%d",&u,&v);
142         AddEdge(u,v);
143         AddEdge(v,u);
144       }
145     for(int i=1;i<=n;i++)
146       scanf("%d",&Val[i]);
147     top[1]=1;
148     Num[1]=++tot;
149     Change(tot,Val[1]);
150     vis[1]=true;
151     dfs1(1,0,1);
152     dfs2(1);
153     scanf("%d",&m);
154     for(int i=1;i<=m;i++)
155       {
156         scanf("%s",s);
157         if(s[1]==‘H‘)
158           {
159             scanf("%d%d",&u,&v);
160             Change(Num[u],v);
161           }
162         else if(s[1]==‘M‘)
163           {
164             scanf("%d%d",&u,&v);
165             printf("%d\n",Query_max(u,v));
166           }
167         else
168           {
169             scanf("%d%d",&u,&v);
170             printf("%d\n",Query_sum(u,v));
171           }
172       }
173     return 0;
174 }

学了个块状树,好写不少,而且常数较小,比链剖慢不了多少。

在dfs时分块,只要当前块满了sqrt(n),就分新的一块。

对每个点,维护从这个点到该块的根(top)的路径上的答案。

更新的时候,只会对 该点 在该块内的子树 造成影响。

询问时,暴力LCA。

这样更新和询问都是O(sqrt(n))的。

Orz zky。

  1 #include<cstdio>
  2 #include<cmath>
  3 #include<algorithm>
  4 using namespace std;
  5 struct Graph
  6 {
  7     int v[60001],first[60001],next[60001],en;
  8     void AddEdge(const int &a,const int &b)
  9     {v[++en]=b;next[en]=first[a];first[a]=en;}
 10 };
 11 Graph G[2];
 12 int fa[30001],dep[30001],top[30001],siz[30001],sz;
 13 int maxv[30001],sumv[30001],w[30001];
 14 int n,x,y,q;
 15 char op[10];
 16 void makeblock(int cur)
 17 {
 18     for(int i=G[0].first[cur];i;i=G[0].next[i])
 19       if(G[0].v[i]!=fa[cur])
 20         {
 21           dep[G[0].v[i]]=dep[cur]+1;
 22           fa[G[0].v[i]]=cur;
 23           if(siz[top[cur]]<sz)
 24             {
 25               siz[top[cur]]++;
 26               top[G[0].v[i]]=top[cur];
 27               G[1].AddEdge(cur,G[0].v[i]);
 28             }
 29           makeblock(G[0].v[i]);
 30         }
 31 }
 32 void dfs(int cur,int Sumnow,int Maxnow)
 33 {
 34     maxv[cur]=Maxnow;
 35     sumv[cur]=Sumnow;
 36     for(int i=G[1].first[cur];i;i=G[1].next[i])
 37       dfs(G[1].v[i],Sumnow+w[G[1].v[i]],max(Maxnow,w[G[1].v[i]]));
 38 }
 39 inline void update(int p,int val)
 40 {
 41     w[p]=val;
 42     if(p==top[p]) dfs(p,val,val);
 43     else dfs(p,val+sumv[fa[p]],max(val,maxv[fa[p]]));
 44 }
 45 inline int Query_max(int u,int v)
 46 {
 47     int res=-2147483647;
 48     while(u!=v)
 49       {
 50           if(top[u]==top[v])
 51             {
 52                 if(dep[u]<dep[v]) swap(u,v);
 53                 res=max(res,w[u]);
 54                 u=fa[u];
 55             }
 56           else
 57             {
 58                 if(dep[top[u]]<dep[top[v]]) swap(u,v);
 59                 res=max(res,maxv[u]);
 60                 u=fa[top[u]];
 61             }
 62       }
 63     return max(res,w[u]);
 64 }
 65 inline int Query_sum(int u,int v)
 66 {
 67     int res=0;
 68     while(u!=v)
 69       {
 70           if(top[u]==top[v])
 71             {
 72                 if(dep[u]<dep[v])
 73                   swap(u,v);
 74                 res+=w[u];
 75                 u=fa[u];
 76             }
 77           else
 78             {
 79                 if(dep[top[u]]<dep[top[v]])
 80                   swap(u,v);
 81                 res+=sumv[u];
 82                 u=fa[top[u]];
 83             }
 84       }
 85     return res+w[u];
 86 }
 87 int main()
 88 {
 89     scanf("%d",&n);
 90     for(int i=1;i<n;i++)
 91       {
 92           scanf("%d%d",&x,&y);
 93           G[0].AddEdge(x,y);
 94           G[0].AddEdge(y,x);
 95       }
 96     sz=sqrt(n);
 97     for(int i=1;i<=n;i++)
 98       {
 99           scanf("%d",&w[i]);
100           top[i]=i;
101           siz[i]=1;
102       }
103     makeblock(1);
104     for(int i=1;i<=n;i++)
105       if(top[i]==i) dfs(i,w[i],w[i]);
106     scanf("%d",&q);
107     for(int i=1;i<=q;i++)
108       {
109           scanf("%s%d%d",op,&x,&y);
110           if(op[1]==‘M‘) printf("%d\n",Query_max(x,y));
111           else if(op[1]==‘H‘) update(x,y);
112           else printf("%d\n",Query_sum(x,y));
113       }
114     return 0;
115 }
时间: 2024-12-26 17:13:19

【块状树】【树链剖分】bzoj1036 [ZJOI2008]树的统计Count的相关文章

[BZOJ1036][ZJOI2008]树的统计Count 解题报告|树链剖分

树链剖分 简单来说就是数据结构在树上的应用.常用的为线段树splay等.(可现在splay还不会敲囧) 重链剖分: 将树上的边分成轻链和重链. 重边为每个节点到它子树最大的儿子的边,其余为轻边. 设(u,v)为轻边,则size(v)<=size(u)/2 (一旦大于了那必然是重边) 也就是一条路径上每增加一条轻边节点个数就会减少一半以上,那么显然根到任意一个节点路径上的轻边条数一定不会超过log(n)(不然节点就没了啊23333) 重链定义为一条极长的连续的且全由重边构成的链. 容易看出重链两两

bzoj1036: [ZJOI2008]树的统计Count(树链剖分+线段树维护)

bzoj1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MB Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的

bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit][Status][Discuss] Description 一 棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

poj 3237 Tree(树链剖分,线段树)

Tree Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 7268   Accepted: 1969 Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edges are numbered 1 through N − 1. Each edge is associated with

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

3626: [LNOI2014]LCA Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1272  Solved: 451[Submit][Status][Discuss] Description 给出一个n个节点的有根树(编号为0到n-1,根节点为0).一个点的深度定义为这个节点到根的距离+1. 设dep[i]表示点i的深度,LCA(i,j)表示i与j的最近公共祖先. 有q次询问,每次询问给出l r z,求sigma_{l<=i<=r}dep[

HDU - 3966 Aragorn&#39;s Story(树链剖分入门+线段树)

HDU - 3966 Aragorn's Story Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of ene

[BZOJ1036/ZJOI2008]树的统计(Count)-树链剖分

Problem 树的统计 题目大意 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 So Lazy No Solution 裸的树链剖分,题解一搜一大把.就连这个都炸了几次.我太菜了... AC

bzoj1036 [ZJOI2008]树的统计Count 树链剖分模板题

[ZJOI2008]树的统计Count Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行

bzoj1036 [ZJOI2008]树的统计Count(树链剖分)

Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条