线段树的入门题

2、BOI 矩形面积并 mar

矩形数量<=10000,0<=坐标分量<=30000。

与USACO PICTURE 差不多,只是现在要用的只有测度。

 

3、PROMOTION (POI0015)

问题大意:

一位顾客要进行n(1≤n≤5000)天的购物,每天他会有一些账单。每天购物以后,他从以前的所有账单中挑出两张账单,分别是面额最大的和面额最小的一张,并把这两张账单从记录中去掉。剩下的账单留在以后继续统计。输入的数据保证,所有n天的账单总数不超过1000000,并且每份账单的面额值是1到1000000之间的整数。保证每天总可以找到两张账单。

 

算法:

本题明显地体现了动态维护的特性,即每天都要插入一些面额随机的账单,同时还要找出最大和最小的两张。不妨建立前面所说的“维护点信息的线段树”,这棵线段树的范围是[1, 1000000],即我们把每种面额设为一个点。

插入和删除一份账单是很容易的。如何找到最大(最小)的账单呢?对于一个树v来说,如果C[LSON[v]]>0,那么树v中的最小值一定在它的左子树上。同样,如果C[RSON[v]]>0,它的最大值在右子树上;否则,如果C[LSON[v]]=0,那么最大最小的两份账单都在右子树上。所以线段树的计数其实为我们提供了线索。显然对于一个特定面额来说。它的插入,删除,查找路径是相同的,长度为树的深度,即log1000000=20。如果总共有m张账单,那么考虑极限时的计算量为m*20+n*20*2。

本题还可以采取巧妙的技巧,线段树不一定要存账单的具体面额。由于我们对1000000种面额都进行了保存,所以线段树显得比较庞大。有时,当值的范围很大,或者值是实数,或者内存有限制时,往往直接用线段树就不行了。

可以采取一种方法:用hash来保存每一种面额的账单数目,然后对于一个具体的账单,例如面额为V,我们在线段树中保存V/100的值,也就是说,我们把连续的100种面额的账单看成是一组。由于V的范围是[1..1000000],所以现在线段树中只有10000个点了。在找最大的数的时候,首先找到最大的组,然后在hash里对这个组进行搜索,显然这个搜索的规模不会超过100。由于线段树变小了,所以树的深度只有14左右,整个问题的复杂度极限为m*14+n*14*100*2,对于问题的规模来说,仍然是高效率的。但这样做比前种方法在一定程度上节省了空间,即线段树部分的内存减少了(但整体还是和m成正比)。

 

4、Balkan 2004 team

BOI(国际半岛信息学奥林匹克)要选拔最优秀的学生代表巴尔干半岛去参加IOI。有N名优秀学生参加选拔赛,从1到N编号, 。考试一共三次,没有任意两名考生在任意一次考试中名次相同。

如果选手A的3次考试名次都在选手B前面,就称A“优于”B

如果对于选手A,没有其他的选手“优于”A,就称A是“优秀”的。

请求出“优秀”的选手数。

 

输入:

第一行是整数N。

下面的3行,每行表示一次考试的选手排名。每行N个数,按照编号表示选手的名次。

 

输出:

一个整数,表示“优秀”的选手数。

 

样例:

输入

3

2 3 1

3 1 2

1 2 3

输出

3

说明

1 2 3都是“优秀”的。

 

输入

10

2 5 3 8 10 7 1 6 9 4

1 2 3 4 5 6 7 8 9 10

3 8 7 10 5 4 1 2 6 9

输出

4

说明

1 2 3 5是“优秀”的。

 

算法:

可以先按照第一次考试的排名作为在“某个数据结构”中插入和查询的顺序,以第二次考试的排名作为“某个数据结构”的下标,以第三次的名次作为“某个数据结构”结点的值,我们每次要询问的是某一段下标中的最小值。这样,“某个数据结构”用线段树再好不过了,每个非叶节点表示的区间存放一个当前线段树中此区间的最小值。

按照第一次考试的排名从先往后,每次插入一个人x的第二次排名 和第三次排名 ,然后询问区间 中的最小值 。如果 ,就说明x不是“优秀”的,因为bx前被插入线段树,三次排名都比x好;如果 ,则说明x是“优秀”,因为前两次竞赛排名比x好的所有选手在第三次竞赛中的排名都没有x好。

每个插入和询问操作都在 时间内完成,所以总的时间复杂度是

 

5、Prince and Princess (UVA 10635) 3 Seconds

In an n x n chessboard, Prince and Princess plays a game. The squares in the chessboard are numbered 1, 2, 3 ... n*n, as shown below:

 

Prince stands in square 1, make p jumps and finally reach square n*n. He enters a square at most once. So if we use xp to denote the p-th square he enters, then x1, x2, ... xp+1 are all different. Note that x1 = 1 and xp+1 = n*n. Princess does the similar thing - stands in square 1, make q jumps and finally reach square n*n. We use y1, y2 , ... yq+1 to denote the sequence, and all q+1 numbers are different.

 

Figure 2 belows show a 3x3 square, a possible route for Prince and a different route for Princess.

 

The Prince moves along the sequence: 1 --> 7 --> 5 --> 4 --> 8 --> 3 --> 9 (Black arrows), while the Princess moves along this sequence: 1 --> 4 --> 3 --> 5 --> 6 --> 2 --> 8 --> 9 (White arrow).

The King -- their father, has just come. "Why move separately? You are brother and sister!" said the King, "Ignore some jumps and make sure that you‘re always together."

 

For example, if the Prince ignores his 2nd, 3rd, 6th jump, he‘ll follow the route: 1 --> 4 --> 8 --> 9. If the Princess ignores her 3rd, 4th, 5th, 6th jump, she‘ll follow the same route: 1 --> 4 --> 8 --> 9, (The common route is shown in figure 3) thus satisfies the King, shown above. The King wants to know the longest route they can move together, could you tell him?

 

Input 

The first line of the input contains a single integer t(1 <= t <= 10), the number of test cases followed. For each case, the first line contains three integers n, p, q(2 <= n <= 250, 1 <= p, q < n*n). The second line contains p+1 different integers in the range [1..n*n], the sequence of the Prince. The third line contains q+1 different integers in the range [1..n*n], the sequence of the Princess.

 

Output 

For each test case, print the case number and the length of longest route. Look at the output for sample input for details.

 

Sample Input            Output for Sample Input

3

3 6 7

1 7 5 4 8 3 9

1 4 3 5 6 2 8 9

10 9 9

1 2 3 4 5 6 7 8 9 10

1 2 3 4 5 6 7 8 9 10

4 2 2

1 2 4

1 3 4

Case 1: 4

Case 2: 10

Case 3: 2

 

 

本题就是要求两个数列ab(最大长度为50000)的最长公共子序列,而且每个数在一个序列中至多出现一次。

我们知道,使用一般的动态规划算法,最好也只能做到 的时间复杂度。但此问题有个特殊性,就是每个数在一个序列中至多出现一次,也就是规定了配对,那么就可以有一种高效的算法。

先根据第二个数列从前往后处理,每次访问到 的时候,直接( )找出 在第一个数列中出现的位置t。接着就是要求b数组下标小于ia数组下标小于t的元素的最长公共子序列的长度。b数组下标小于i可以通过插入操作来保证,关键就是每次要求a数组下标小于t的元素的“某一个数值”的最大值。要完成这样的操作,可以使用线段树等高效的数据结构,每次插入和询问区间最大值都是 的,总的时间复杂度就将为

 

6、usaco 2004 april open moofest

直线上有N个点,1<=N<=20000。给出每个点的坐标x,x是整数,1<=x<=20000,x各不相同。每个点又有一个权值v(i),也在1到20000之间。对于任意两个点x1和x2,x1<x2,他们的分值等于(x2-x1)*max{v(x1), v(x2)}。求任意两点分值的总和。

例如

输入

4

3 1 {第一个是权值,第二个是坐标}

2 5

2 6

4 3

输出

57

 

算法:

先将所有点按照权值从小到大排序,然后依次处理。

建立一棵线段树,区间表示原来点的坐标区间。每个节点(x,y)表示区间,根节点表示区间[1,20000]。如果区间[x,y]中x=y那么它就是叶结点,否则它有两个子节点,分别表示区间:[x, (x+y) div 2]和[(x+y) div 2 + 1, y]。

每个节点还要记录两个值:L,表示当前这个区间内的所有点到区间左端点的距离和;R,表示当前这个区间内的所有点到区间右端点的距离和。

每次处理一个点,就要求出前面的点到它的距离总和,这可以在O(logN)的时间内完成。然后,将这个点插入到线段树中,并作更新。这个操作时间复杂度也是O(logN)。

所以,总的时间复杂度是O(N*logN)。

 

 

7、USACO 2004 DEC obstacle

有N条栏杆,从下往上编号1到N。最下面还有一条栏杆,上面有一个目标位置*,*是坐标轴的原点(0, 0)。如下图所示:

   +-S-+-+-+        (fence #N)
 +-+-+-+            (fence #N-1)
     ...               ...
   +-+-+-+          (fence #2)
     +-+-+-+        (fence #1)
=|=|=|=*=|=|=|      (barn)
-3-2-1 0 1 2 3    

现在从栏杆N的S位置出发,选择向左还是向右走,走到栏杆边界时,马上往下走。一直走到碰到下一个栏杆,再决定向左还是向右走,走到栏杆边界时再往下走,……这样一只走,最后要走到标有*的目标位置。

单位长度都是1,每条栏杆的纵向距离为1,问从S走到*的最短左右移动总距离。

输入:

第一行是整数N和S,这里的S表示起点的横坐标。1<=N<=50000,-100000<=S<=100000。

接下来的N行,分别描述1到N栏杆的左右端点横坐标Ai和Bi。-100000<=Ai, Bi<=100000。保证AN<=S<=BN。

输出:

从S走到*的最短左右移动总距离。

样例:

输入

4 0

-2 1

-1 2

-3 0

-2 1

输出

4

解释:

如图

   +-+-S-+             Fence 4

 +-+-+-+               Fence 3

     +-+-+-+           Fence 2

   +-+-+-+             Fence 1

 |=|=|=*=|=|=|         Barn

-3-2-1 0 1 2 3 

移动顺序:右下下右下下左左,左右移动共4次。

 

算法:

最关键的是要快速的求出从一条栏杆的边界往下走,最早碰到的是下面的哪一条栏杆。如果从下往上处理的话,每次要查找两个端点属于之前的哪个区间,然后插入这条栏杆所表示的区间。所以应该采用线段树来维护信息。

树的每个节点要存放一个区间编号,如果整段被多个不同的区间覆盖可以设为一个特殊值。

 

8、Mipt 037

有N种货物,每种货物有3个属性:Pi表示运送一个单位这种货物可获得的利润;Mi表示一个单位这种货物的重量;Qi表示这种货物的数量。你允许运送总重量在A到B之间的货物,问最多的利润和一种方案。N ≤ 100,1 ≤ AB ≤ 10000。

 

算法:

这道题是dp+线段树。先讨论不用线段树的情况,即一般的dp。

此时就是比较典型的背包问题了,有一个伪多项式算法。即用f[i,j]表示前i个货物,重量达到j时能有的最大值。若用这个算法,复杂度将为O(n*b*q),肯定会超时。

考虑此算法种状态转移的部分

For i:=1 to n do

  For j:=0 to b do

    f[i,j]:=max(f[i-1,j-m[i]*k]+p[i]*k)       (k=0 .. q[i])

状态转移与q[i]有关,但发现j-m[i]*k (k=0 .. q[i]) 模m[i]和j同余。于是对于每个i,所有的j以对m[i]的模分组考虑,使用一种数据结构,要使得转移时间变成log级别。

如果j是从小到大处理的,那么对于j+m[i],它可选的值就是以前和它一组的那些。当然每个值都要加上p[i],这样处理会使得复杂度又回到原来的,我们就每次将新增的值减掉p[i],最后用的时候再补回来。

每次都先求一下j mod m[i]这一组的最大值,再适当地补一个值即可。再将f[i-1,j]减去p[i]后插入本组。要高效地完成这些操作,线段树是一种选择。

算法的大致框架为:

for i:=1 to n do begin

  构造关于i的m[i]棵线段树的骨架

  for j:=0 to b do begin

    在自己的组的线段树中求出f[i,j]的实际值

    插入f[i-1,j]-p[i]到自己的组的线段树中

    end;

  end;

 

9、zju 1201 Inversion

定义一个序列A,是1..n的一个排列,序列B表示A中的每个元素左侧比它大的元素的个数。根据序列A来构造序列B的操作叫P,反过来叫I。输入A或B和操作,输出另一个序列。

 

算法:

P操作:可以按照原先A的顺序处理,将Ai的值作为索引加入线段树,[Ai+1..n]中有多少元素即为Bi

I操作:将1..n加入线段树,读入一个Bi,就查找一个位置p,使得[1..p]有Bi+1个元素,在位置p就是i。

 

 

习题

1、Zju 1319

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值