码迷,mamicode.com
首页 > 其他好文 > 详细

NOIP2011 选择客栈 题解(最简方法,超短代码)

时间:2015-08-29 18:56:26      阅读:173      评论:0      收藏:0      [点我收藏+]

标签:

描述

丽江河边有n家很有特色的客栈,客栈按照其位置顺序从1到n编号。每家客栈都按照某一种色调进行装饰(总共k种,用整数0~ k-1表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。

两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过p。

他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过p元的咖啡店小聚。

格式

输入格式

第一行三个整数n,k,p,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值;
接下来的n行,第i+1行两个整数,之间用一个空格隔开,分别表示i号客栈的装饰色调和i号客栈的咖啡店的最低消费。

输出格式

输出只有一行,一个整数,表示可选的住宿方案的总数。

样例1

样例输入1[复制]

5 2 3
0 5
1 3
0 2
1 4
1 5

样例输出1[复制]

3

限制

1s

提示

对于30%的数据,有n≤100;
对于50%的数据,有n≤1,000;
对于100%的数据,有2≤n≤200,000,0<k≤50,0≤p≤100,0≤最低消费≤100。

解一:

对于每个客栈(最低消费 v,格调 c),在第二个人的旅店之前的、出现的所有跟该客栈同格调的客栈、符合旅客要求的前提是 :
 在这两客栈间存在一家最低消费<= p 的客栈,记 t 为离当前决策客栈、最近的、最低消费 <= p的客栈编号,则当前
决策点之前、能与该点组成符合要求的客栈即为:前 f 家客栈中、与该决策客栈同格调的客栈。
 所以思路就很明显了:开一个 allsameclour[0..50] 的数组记录前 f 家客栈中格调为 x的数量,记为allsameclour[x]  ;则、当前决策点能增加
的方案数为allsameclour[x] ,每次只要把 answer 的值加上 allsameclour[决策客栈]的值即可。
也就是完成 allsameclour 这个数组的维护。 所以我们再开一个beforesameclour [0..50] 数组来记录:当前决策点前并且格调为 x的客栈数量,记为beforesameclour[x] ; 

 用lastsameclour[0..50] 来记录:上一个、格调为 x的客栈编号,记为 lastsameclour[x]。那么、每次决策时,只要先更新 t的值(即如果该决策点、符合最低消费要求,则将最近消费点t、更新为当前决策点);再判 断如果 t >= lastsameclour[当前决策点格调] ,即上一个和决策点同格调的客栈编号,那么说明:离该点最近的、同格调的客栈、与决策点之间、有符合最低消费的客栈。那么就可以将 allsameclour[x]的值更新为beforesameclour[x] (x为当前决策点格调),因为出现的x格调都可记入 方案数。

var

 allsameclour ,tot,can:array[0..51] of longint;                                   //3个数组上面都有提到
      n,k,p,i,answer,flag,color,cost:longint;                                //answer记录总方案数,flag为当前决策之前最近的消费点


      begin
        readln(n,k,p);
        for i := 1 to n do begin
          readln(color,cost);                                                      //采用边读边做,因为所有有用信息均有记录
          if cost <= p then flag := i;                                           //更新最近消费点
          if flag >= lastsameclour[color] then 
           allsameclour [color]:=beforesameclour[color];            //更新allsameclour  数组的值
          inc(answer,allsameclour [color]);                                                //记录前 i 点、与第 i 点、构成的、符合要求的方案数
          inc(beforesameclour[color]);                                                            //更新beforesameclour 数组的值
         lastsameclour[color]:=i;                                                              //更新 lastsameclour数组的值
      end;


      writeln(answer);




     

解二:

用 i 枚举 后一个人住的旅馆
i = 1 →n
pre 表示 在 i 旅店之前 有合适酒店可选的 最后一个旅店
rank 表示 当前旅店在同颜色旅店中的排位
last 表示 此颜色 在i之前 最后一个旅店
can 表示 在 i 旅店之前 离他最近 的酒店
num 表示 第二个人住 i 旅店时,第一个人可能的旅店个数。
can[i]=i(p<=costnow)
 can(p>cost[now])

pre[i]=last(last<=can[i])
 pre[last](last>=can[i])

num[i]=rand[pre[i]]

答案就是 ∑num[i]
时间复杂度 O(n)
空间复杂度 O(n)
在线做 离线做 都可以

解三:

f[i] 表示以i结尾的方案的个数
back[i]表示同种颜色上一次出现的位置那么
num[i]表示 i之前的店当中 和i颜色相同的个数

答案则是sum(f[i])

转移方程为
f[i] = num[i](min[back[i],i]<=k )
f[i] = f[back[i]] (min[back[i],i]>k)

令r[i]=min[back[i],i] 
r数组可以在 o(k*n)的时间内预处理出来
总复杂度是O(k*n)

解四:
还有一种O(nlogn)的方法:用list[i,j]表示颜色为i的第j个客栈,也就是将客栈按照颜色紧缩存储。另用pos[i]表示第i个旅馆在list [color[i]]中的位置。用线段树/ST算法(推荐)预处理出区间消费的最小值,也就是min{cost[i..j]},易得到 min[k,i]是非增的,注意这是后面二分的关键。然后枚举第二个人,在list[color[i]]中用二分找到一个j满足 min[j,i]<=P,那么ans=ans+j,因为list[color[i],1..j]中必然都是颜色为color[i],且区间最小值也都<=P。



版权声明:本文为博主原创文章,未经博主允许不得转载。

NOIP2011 选择客栈 题解(最简方法,超短代码)

标签:

原文地址:http://blog.csdn.net/zzx2015/article/details/48088333

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!