zoukankan      html  css  js  c++  java
  • Vijos1448校门外的树 题解

    Vijos1448校门外的树 题解

    描述:
    校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……
    如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:
    K=1,K=1,读入l、r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同
    K=2,读入l,r表示询问l~r之间能见到多少种树
    (l,r>0)
     
    输入格式:
    第一行n,m表示道路总长为n,共有m个操作
    接下来m行为m个操作
     
    输出格式:
    对于每个k=2输出一个答案
     
    样例输入:
    5 4
    1 1 3
    2 2 5
    1 2 4
    2 3 5
     
    样例输出:
    1
    2
     
    数据范围:
    20%的数据保证,n,m<=100
    60%的数据保证,n <=1000,m<=50000
    100%的数据保证,n,m<=50000
     
     
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~
     
    分析:
    这道题是一个典型的区间问题,考虑到数据量较大,使用线段树完成这个操作。由于树的种类很多,不难想到用线段树暴力维护的方法。但是暴力维护一定会超时,那么这么解决这个问题呢?
    这里介绍一种十分机智的想法——括号序列
    假设有一个长度为10的数轴,我们要将区间[ 2 , 5 ]中种树,这时,我们将 2 处放一个左括号 " ( "  ,5处放一个 " )"  ,表示区间 [ 2 , 5 ]种了树。
    查询某个区间树的种类,如区间[ 3 , 10],只需统计10之前(包括10)有多少个‘(’,统计3之前有多少个‘)’,(不包括3)。  
    如下图所示:

    以上就是括号序列的过程。简单的说,就是更新区间[a,b]时,点a记录左括号数,点b记录右括号数,查询区间[a,b]时,即为b之前(包括b)的左括号数-a之前的右括号数。
     
    下面贴注释代码:
     
     1 #include "bits/stdc++.h"
     2 #define maxN 50010
     3 
     4 using namespace std ;
     5 typedef long long QAQ ;
     6 
     7 struct Tree
     8 {
     9     int l , r ;
    10     QAQ liml , limr ;//左括号右括号
    11 };
    12 
    13 Tree tr[maxN << 2];
    14 
    15 void Build_Tree ( int x , int y , int i )//建树
    16 {
    17     tr[i].l = x ;
    18     tr[i].r = y ;
    19     if( x == y )return ;
    20     else
    21     {
    22         QAQ mid = (tr[i].l + tr[i].r) >> 1 ;
    23         Build_Tree ( x , mid , i << 1);
    24         Build_Tree ( mid + 1 , y , i << 1 | 1);
    25         return ;
    26     }
    27 }
    28 
    29 void Update_left ( int w , int i )
    30 {
    31     if( w == tr[i].l && w == tr[i].r )tr[i].liml++;//找到目标节点
    32     else
    33     {
    34         QAQ mid = (tr[i].l + tr[i].r) >> 1 ;
    35         if( w > mid )Update_left( w , i << 1 | 1);//找右儿子
    36         else if( w <= mid)Update_left( w , i << 1 );//找左儿子
    37         tr[i].liml = tr[i << 1].liml + tr[i << 1 | 1].liml ;//回溯更新
    38     }
    39 }
    40 
    41 void Update_right ( int w , int i )//同Update_left
    42 {
    43     if( w == tr[i].l && w == tr[i].r )tr[i].limr++;
    44     else
    45     {
    46         QAQ mid = (tr[i].l + tr[i].r) >> 1 ;
    47         if( w > mid )Update_right( w , i << 1 | 1);
    48         else if( w <= mid)Update_right( w , i << 1 );
    49         tr[i].limr = tr[i << 1].limr + tr[i << 1 | 1].limr ;
    50     }
    51 }
    52 
    53 QAQ Query_left ( int q , int w , int i )//同Query_right
    54 {
    55     if( q <= tr[i].l && w >= tr[i].r )return tr[i].liml ;
    56     else
    57     {
    58         QAQ mid = (tr[i].l + tr[i].r) >> 1 ;
    59         if ( q > mid )return Query_left ( q , w , i << 1 | 1);
    60         else if ( w <= mid ) return Query_left ( q , w , i << 1);
    61         else return Query_left ( q , w , i << 1 | 1) + Query_left ( q , w , i << 1);
    62     }
    63 }
    64 
    65 QAQ Query_right ( int q , int w , int i )
    66 {
    67     if( q <= tr[i].l && w >= tr[i].r )return tr[i].limr ;//找到目标区间直接返回
    68     else
    69     {
    70         QAQ mid = (tr[i].l + tr[i].r) >> 1 ;
    71         if ( q > mid )return Query_right ( q , w , i << 1 | 1);//找右儿子
    72         else if ( w <= mid ) return Query_right ( q , w , i << 1);//找左儿子
    73         else return Query_right ( q , w , i << 1 | 1) + Query_right ( q , w , i << 1);//左右儿子都查找
    74     }
    75 }
    76 
    77 int main()
    78 {
    79     int N, M, op, ll, rr ;
    80     scanf("%d %d", &N, &M);
    81     Build_Tree ( 1 , N , 1 ) ;//建树
    82     while(M--)
    83     {
    84         scanf("%d%d%d", &op, &ll, &rr);
    85         if( op == 1 )
    86         {
    87             Update_left ( ll , 1);//添加左括号
    88             Update_right ( rr , 1 );//添加右括号
    89         }
    90         else
    91         {
    92             QAQ ans = Query_left( 1 , rr , 1);
    93             if (ll != 1)ans -= Query_right(1 , ll - 1 , 1);//当ll不等于1时再相减,否则栈会炸
    94             printf("%I64d
    ", ans);
    95         }
    96     }
    97     return 0 ;
    98 }
     PS: 本题也可以用树状数组完成,代码量较少,容易实现。
     
     (完)
     
  • 相关阅读:
    xhtml中隐藏滚动条
    一个题目包括70种网页制作常用技巧 (转)
    Oracle 数据类型映射 (ADO.NET)
    oracle数据库 数据类型
    VisualSVN Server的配置和使用方法
    oracle 权限管理
    项目管理实践【三】每日构建【Daily Build Using CruiseControl.NET and MSBuild】
    showModalDialog和showModelessDialog使用心得
    Bug跟踪管理【Bug Trace and Management】
    开发WAP站点之使用PC电脑浏览器访问WAP手机站点 (转)
  • 原文地址:https://www.cnblogs.com/shadowland/p/5870395.html
Copyright © 2011-2022 走看看