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