• 线段树之动态开点  HDU 6183 Color it.


    在谈线段树的动态开点之前先说说线段树吧。线段树是用于区间维护,为节省时间而牺牲空间的二叉搜索树,它有几个缺点,先说三个:第一个是区间维护之后是不可逆的,比如我第7次修改了区间内容后,返回想看一下我在第5次修改之前的数据就看不鸟了,好可恶~;第二个是空间牺牲太大,虽然说时间和空间不可得兼,但是维护一个区间,需要消耗4倍区间的内存还是有点多了,而且很多时候开出来的点还没有被用到,浪费了空间,好可恶~;第三个是第二个问题衍生出来的,因为开的空间很大,一道题基本上建几棵树就把内存用完了,可恶可恶~,我要森林!于是,算法进阶一下:节省空间——线段树的动态开点!

    什么是线段树的动态开点?借用前辈们的一句话便是:开局一个根,装备(枝叶)全靠给。详细用下面这道题来解释吧。

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6183

    题目大意:在1-1e6的矩形坐标上进行有4个操作可以进行:操作0(输入0)清空坐标系;操作1(输入1,x,y,c)在(x,y)上添加颜色c ;操作2(输入2,x,y1,y2).查询(1,y1),(x,y2)构成的矩形区域内的颜色种类数量;操作3(输入3)退出。

    数据大小:1≤x,y≤1e6,0≤c≤50,操作数量为150000,清空次数不会超过10次。

    解题思路:当只有一种颜色时,区间搜索可以用线段树来写:因为x轴每次都是从1开始,而y轴是不断在变化的区间,用线段树来维护y轴,树中存入一段区间内上色的最小x坐标,所以当给定一段y轴区间和一段x区间(1-m)的区间时,只要查询到对应的y区间内m>x即可以说明该面积内有上色。但是此题有50种颜色,则需要建立50棵树去维护,看数据大小,显然是不可行的。

    线段树动态开点比动态树少了建树一个模块,因为动态开点不需要直接建树,而是在更新部分建树,另外,动态开点是从上往下开,首先是将整个大区间建一个点,然后更新哪个点就往哪个方向建点,这里注意的是这些点不是像二叉树一样每个角标是有规律的,因为边开边建,所以先开的点在一起,比如可能根节点是Node[1],右儿子为Node[2],右儿子的右儿子为Node[3],再建左边则左儿子为Node[4]...所以说开的点为事先定义的数组Node;而把他们构成树是用Node中的lson和rson,比如Node[1].rson=2,Node[1].lson=4;表示Node[1]点的右子树和左子树为点2和点4。这样就很好满足了用几个点就开几个点的条件了。接下来可以根据下面ac代码来看看,自己觉得注释的蛮细的(数据太多,用c输入输出,c++超时)

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 using namespace std;
      5 const int SIZE=1000000+5;
      6 int INF=SIZE+5;
      7 int num;    //num用于开点用,为开点的角标 
      8 int root[55];    //树的根结点。 
      9 bool key;    //key用于判断区域内是否涂色,true是false否。 
     10 struct    Node{
     11     int l,r,lson,rson;    //l,r节点的左右区间和;lson,rson左右儿子节点角标 
     12     int minx;    //minx即该区间涂色点的最小x坐标 
     13 }Node[SIZE<<2];
     14 int min(int a,int b)
     15 {
     16     return a>b?b:a;
     17 }
     18 
     19 void push(int n,int l,int r,int x)    //更新角标为n的点信息 
     20 {
     21     Node[n].l=l;
     22     Node[n].r=r;
     23     Node[n].minx=min(Node[n].minx,x);
     24     return;
     25 }
     26 
     27 void update(int &rt,int l,int r,int x,int y) 
     28 {
     29     if(!rt) rt=++num;    //如果这个点不存在,就开一个新点,如果存在,就更新存在的那一点。 
     30                         //rt取的地址有两种情况,一是根节点root[i],另一个就是Node[i]的左右儿子 
     31     push(rt,l,r,x);     //更新这个点的信息 
     32     if(l==r) return;
     33     //{    cout<<"<---"<<l<<"---"<<r<<"--->"<<endl;return; }     //更新到叶子节点结束 
     34     int mid=(l+r)>>1;
     35     if(mid>=y)    //只开包含y的一边 
     36         update(Node[rt].lson,l,mid,x,y);    //开左子树 
     37     else
     38         update(Node[rt].rson,mid+1,r,x,y);    //开右子树 
     39 }
     40 void query(int rt,int l,int r,int x)
     41 {
     42     if(key||!rt) return;    //如果rt点没有被开发或者已经找到就直接退出
     43     //cout<<"!!!"<<rt<<" ##"<<Node[rt].l<<" ##"<<Node[rt].r<<"!!!"<<endl;
     44     if(Node[rt].l>=l&&Node[rt].r<=r)      
     45     {
     46         if(Node[rt].minx<=x) //如果最小的minx不超过x,则说明1-minx在1-x里面,即1-x内有涂色 
     47             key=true;
     48         return;
     49     }
     50     int mid=(Node[rt].l+Node[rt].r)>>1;
     51     if(mid>=l)
     52         query(Node[rt].lson,l,r,x);
     53     if(r>mid)
     54         query(Node[rt].rson,l,r,x);
     55     return; 
     56 } 
     57 void init()    //初始化函数
     58 {
     59     num=0;    //开的点从0开始开点 ,所以一开点角标就是非0了 
     60     memset(root,0,sizeof(root));    //根节点都赋值0
     61     for(int i=0;i<(SIZE<<2);i++)
     62     {
     63         Node[i].rson=Node[i].lson=0;    //每个点都未用,左右儿子都为0 
     64         Node[i].minx=INF;    //每个点Node点代表一个区间,未用该点,该区间minx不存在,即赋值INF 
     65     } 
     66     return; 
     67 }
     68 int main()
     69 {
     70     int op,a,b,c;    //op为操作operate 0 1 2 3 
     71     init();
     72     while(~scanf("%d",&op)/*cin>>op*/&&op-3)
     73     {
     74         if(op==0)
     75             init();
     76         else if(op==1)
     77         {
     78             scanf("%d %d %d",&a,&b,&c);
     79         //cin>>a>>b>>c;
     80             update(root[c],1,1000000,a,b);
     81         //    cout<<"-----------------------------------------"<<endl;
     82         //    cout<<"目前有多少个节点:"<<num<<endl;
     83         //    for(int i=0;i<=num;i++)
     84         //    cout<<i<<">> l:"<<Node[i].l<<" r:"<<Node[i].r<<" lson:"<<Node[i].lson<<" rson:"<<Node[i].rson<<" minx:"<<Node[i].minx<<endl; 
     85         //    cout<<"-----------------------------------------"<<endl;
     86         //    for(int i=0;i<=50;i++)
     87         //    cout<<root[i]<<" ";
     88         //    cout<<endl; 
     89         //    cout<<"-----------------------------------------"<<endl;
     90         }
     91         else if(op==2)
     92         {
     93             scanf("%d %d %d",&a,&b,&c);
     94             //cin>>a>>b>>c;
     95             int ans=0;
     96             for(int i=0;i<=50;i++)    //遍历50棵线段树
     97             {
     98                 key=false;
     99                 query(root[i],b,c,a);    //访问第i棵树区间b,c内1到a有没有第i种颜色
    100                 if(key)
    101                     ans++;
    102             }
    103             printf("%d
    ",ans);
    104         //    cout<<ans<<endl;
    105         }
    106     }
    107     return 0;
    108 } 
  • 相关阅读:
    NSLocalizedString用法
    4-27学习心得
    手势学习
    plist处理
    数据存储
    initWithFrame方法
    控制器跳转小常识
    UIGestureRecognizer学习笔记
    博客资源
    检测手机wifi有没有打开
  • 原文地址:https://www.cnblogs.com/wwq-19990526/p/10561306.html
Copyright © 2020-2023  润新知