【BZOJ】【1036】树的统计

  嗯这题是一道对树进行动态修改&查询的经典题目,可以拿来练习树链剖分~

  啊对于这种动态修改&查询的题目,我们最喜闻乐见的就是在一个序列上去做了,毕竟可以直接套各种数据结构模版啊,比如线段树、平衡树之类的。那么对于这种树上的动态修改&查询,我们可以把它通过一定的手段,“转化”成序列上的问题,再套用xx树之类的数据结构进行快速维护。而这个手段呢,就有很多种了(应该是吧?),这里用到的树链剖分,就是一种将树转化成序列的划分方式。

  好的,我们现在拿到一棵树,首先我们会看到,这个树跟序列几乎没半点长的像的地方T_T,除非当这个坑爹的树刚好是一条链的形状……诶等等?链?对,就是那个极端情况下平衡树会退化成的那种样子 —— 一条链= = 联想到了什么?没错,我们可以把树拆成一条条链,嗯,我们可以这样想像一下:首先我们手里有一条链,然后我们再拿过来一条链,把它接在链中间的某个位置上,它就有了个分支,然后我们再接几条链上来,诶没错,它就成了一棵树!(怎么感觉有点像鸡毛掸子似的)

  也就是说,我们可以将一棵树拆成几条链,平放在一条线上,它就成了一个序列了~

  现在问题来了:怎么拆?首先我们在树上进行的查询,经常是对于两点间的【路径】的查询,那么我们肯定希望我们拆出来的链,尽可能是连续的大段,而不是细碎的小段,因为我们在用数据结构维护的时候,肯定是维护树上连续的链比较方便。而这种“长链”,就是在树链剖分中我们称之为【重链】的东西。但是,如果只有一条条重链也组不成一棵树啊,所以我们需要【轻链】来将重链连接起来,这样,我们对于树上所有的点和边,就都划分开了。

  这个工作我们可以通过两次dfs来完成:一次dfs求出所有节点的father,son(这个son专指重儿子,重儿子的重儿子连下去组成重链),size,deep求出来……

  (此处省略500字)好吧其实我还是有节操一点,把实现过程传送一下吧:http://blog.sina.com.cn/s/blog_6974c8b20100zc61.html

  思想就是:路径可以划分为一条或多条重链的加和。(仅考虑【点】)

  不在同一重链上,就往同一条重链上靠,这个是让深度大的往深度小的上面靠(想一想,为什么?),同时对深度大的重链上的值进行 维护or查询;如果在同一重链上,就回归了我们熟知的序列上的 维护or查询 问题了。

  以下是BZOJ1036的代码:

  1 //BZOJ 1036
  2 #include<vector>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<cstdlib>
  6 #include<iostream>
  7 #include<algorithm>
  8 #define rep(i,n) for(int i=0;i<n;++i)
  9 #define F(i,j,n) for(int i=j;i<=n;++i)
 10 #define D(i,j,n) for(int i=j;i>=n;--i)
 11 #define pb push_back
 12 using namespace std;
 13 const int N=30010,INF=~0u>>2;
 14 typedef long long LL;
 15 //#define debug
 16 struct Tree{
 17     int max,sum;
 18     #define L o<<1
 19     #define R o<<1|1
 20 }t[N<<2];
 21 vector<int>G[N];
 22 int n,m;
 23 int tid[N],top[N],fa[N],son[N],dep[N],cnt,tot,size[N],a[N];
 24 bool vis[N];
 25
 26 //从这里到undef为线段树上的操作
 27 #define mid (l+r>>1)
 28 inline void maintain(int o,int l,int r){
 29     t[o].max=t[o].sum=0;
 30     if(l<r){
 31         t[o].max=max(t[L].max,t[R].max);
 32         t[o].sum=t[L].sum+t[R].sum;
 33     }
 34 }
 35
 36 void updata(int o,int l,int r,int pos,int v){
 37     if (l==r) t[o].max=t[o].sum=v;
 38     else{
 39         if (pos<=mid) updata(L,l,mid,pos,v);
 40         if (pos>mid) updata(R,mid+1,r,pos,v);
 41         maintain(o,l,r);
 42     }
 43 }
 44 int ql=0,qr=0;
 45 int _max,_sum;
 46 void query_it(int o,int l,int r){
 47     if (ql<=l && qr>=r){
 48         _max=max(_max,t[o].max);
 49         _sum+=t[o].sum;
 50     }
 51     else{
 52         if (ql<=mid) query_it(L,l,mid);
 53         if (qr>mid) query_it(R,mid+1,r);
 54     }
 55 }
 56
 57 #undef mid
 58 //线段树end
 59
 60 void dfs(int x,int father,int deep){//第一次dfs
 61     vis[x]=1;
 62     fa[x]=father; dep[x]=deep; size[x]=1; son[x]=0;
 63     int maxsize=0;
 64     rep(i,G[x].size()){
 65         int to=G[x][i];
 66         if (vis[to]) continue;
 67         dfs(to,x,deep+1);
 68         size[x]+=size[to];
 69         if (size[to]>maxsize) maxsize=size[to],son[x]=to;
 70     }
 71 }
 72
 73 void connect(int x,int f){//第二次dfs,进行重链连接
 74     vis[x]=1;
 75     tid[x]=++tot; top[x]=f;
 76     if (son[x]) connect(son[x],f);
 77
 78     rep(i,G[x].size()){
 79         int to=G[x][i];
 80         if (!vis[to]) connect(to,to);
 81     }
 82 }
 83
 84 void query(int x,int y){//树上查询
 85     while(top[x]!=top[y]){//如果不在同一重链上
 86         if (dep[top[x]]<dep[top[y]])
 87             swap(x,y);//找到深度大的
 88         ql=tid[top[x]]; qr=tid[x];
 89         query_it(1,1,n);//查询这条重链
 90         x=fa[top[x]];//往深度浅的靠
 91     }
 92     //直到在同一重链上,循环结束
 93     if (dep[x]>dep[y]) swap(x,y);
 94     ql=tid[x]; qr=tid[y];
 95     query_it(1,1,n);//查询这段区间
 96 }
 97
 98 int main(){
 99     #ifndef ONLINE_JUDGE
100     freopen("file.in","r",stdin);
101 //    freopen("file.out","w",stdout);
102     #endif
103     int x,y;
104     scanf("%d",&n);
105     F(i,2,n){
106         scanf("%d%d",&x,&y);
107         G[x].pb(y);
108         G[y].pb(x);
109     }
110     F(i,1,n) scanf("%d",&a[i]);
111     memset(vis,0,sizeof vis);
112     dfs(1,0,1);
113     memset(vis,0,sizeof vis);
114     connect(1,1);
115     F(i,1,n) updata(1,1,n,tid[i],a[i]);
116
117     scanf("%d",&m);
118     char cmd[5];
119     F(i,1,m){
120         scanf("%s%d%d",cmd,&x,&y);
121         if (cmd[1]==‘H‘)
122             updata(1,1,n,tid[x],y);
123         else{
124             _sum=0;
125             _max=-INF;
126             query(x,y);
127             if(cmd[1]==‘M‘) printf("%d\n",_max);
128             else printf("%d\n",_sum);
129         }
130     }
131     return 0;
132 }
133 /******************************************************
134 树链剖分啊,感觉上也是一种把树转化为序列进行操作的过程
135 一棵树不好存,就拆成一条条链,平放在一起就成了一个序列
136 然后根据一定的方式来拆,可以保证链的数量尽量少(log(n))
137 然后就可以用序列操作的方式,对树进行操作了!
138 ******************************************************/ 

时间: 2024-10-20 02:07:32

【BZOJ】【1036】树的统计的相关文章

BZOJ 1036 树的统计-树链剖分

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

BZOJ 1036 树的统计

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之间有一条

BZOJ 1036 树的统计(树链剖分)

PS:树链剖分的很基本的题 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 #define dec(i, a, b) for (int i(a); i >= (b); --i) 7 #define lson i << 1, L, mid 8 #define rson i << 1 | 1,

bzoj 1036 树的统计Count (树链剖分+线段树)

题目大意:给你一棵树,每个点都有点权 有3种操作,修改某节点的权值,求树链上节点的权值的最大值,求树链上节点的权值和 树剖裸题,搜一个树链剖分序,用线段树维护一下即可,总时间 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <queue> 5 #define inf 0x3f3f3f3f 6 #define ll long long 7 #define N

HYSBZ 1036 树的统计Count(树链剖分)

HYSBZ 1036 树的统计Count 题目链接 就树链剖分,线段树维护sum和maxx即可 代码: #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int N = 30005; int dep[N], fa[N], son[N], sz[N], top[N], id[N], idx, val[N];

HYSBZ - 1036 树的统计Count 树链剖分 求和+最大值

好水0.0 #include<cstdio> #include<cstring> #include<cmath> #include<iostream> #include<algorithm> #include<set> #include<map> #include<queue> #include<vector> #include<string> #define eps 1e-12 #de

BZOJ 1036 树链剖分模板题

BZOJ 1036 题意:一棵树,每个点有权值,三种操作:修改一个点的值:询问一条链上最大值:询问一条链上权值和. tags:模板题 // bzoj 1036 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define FF(i,a,b) for (int i=a;i<=b;i++) #define F(i,b,a)

spoj 375 AND bzoj 1036 树链剖分

树链剖分的入门题目,自己写了下感觉还是挺好写的,不过真的有点长... spoj 375 边有权值: 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 6 const int INF = -999999999; 7 const int N = 10001; 8 int head[N]; 9 int sz[N]; 10 int depth[N]; 1

bzoj 1036 树链剖分+线段树 裸题

HYSBZ - 1036 题意:中文题 思路:树链剖分裸题,线段树写得比较搓,(在线段树上修改节点u的时候应该修改u映射到线段树后的节点序号,这里wa了半年,真的是半年) AC代码: #include "iostream" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector

HYSBZ 1036 树的统计Count 树链剖分 线段树

傻缺模板题,练手速和正确率用.. #include <cstdio> #include <cstring> #include <algorithm> #include <map> #include <set> #include <bitset> #include <queue> #include <stack> #include <string> #include <iostream>