NOIP2017 普及组 T4
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 n n n 个格子,这些格子都在同一条直线上。每个格子内有一个数字(整数),表示到达这个 格子能得到的分数。玩家第一次从起点开始向右跳,跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:
玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d d d。小 R 希望改进他的机器人,如果他花 g g g 个金币改进他的机器人,那么他的机器人灵活性就能增加 g g g,但是需要注意的是,每 次弹跳的距离至少为 1 1 1。具体而言,当 g < d g
现在小 R 希望获得至少 k k k 分,请问他至少要花多少金币来改造他的机器人。
第一行三个正整数 n , d , k n,d,k n,d,k ,分别表示格子的数目,改进前机器人弹跳的固定距离,以及希望至少获得的分数。相邻两个数 之间用一个空格隔开。
接下来 n n n 行,每行两个整数 x i , s i x_i,s_i xi,si ,分别表示起点到第 i i i 个格子的距离以及第 i i i 个格子的分数。两个数之间用一个空格隔开。保证 x i x_i xi 按递增顺序输入。
共一行,一个整数,表示至少要花多少金币来改造他的机器人。若无论如何他都无法获得至少 k k k 分,输出 − 1 -1 −1。
7 4 10
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
2
7 4 20
2 6
5 -3
10 3
11 -3
13 1
17 6
20 2
-1
花费 2 2 2 个金币改进后,小 R 的机器人依次选择的向右弹跳的距离分别为 $ 2, 3, 5, 3, 4,3$,先后到达的位置分别为 2 , 5 , 10 , 13 , 17 , 20 2, 5, 10, 13, 17, 20 2,5,10,13,17,20,对应$ 1, 2, 3, 5, 6, 7$ 这 6 6 6 个格子。这些格子中的数字之和 $ 15$ 即为小 R 获得的分数。
由于样例中 7 7 7 个格子组合的最大可能数字之和只有 18 18 18,所以无论如何都无法获得 20 20 20 分。
本题共 10 组测试数据,每组数据等分。
对于全部的数据满足 1 ≤ n ≤ 5 × 1 0 5 1 \le n \le 5\times10^5 1≤n≤5×105, 1 ≤ d ≤ 2 × 1 0 3 1 \le d \le2\times10^3 1≤d≤2×103, 1 ≤ x i , k ≤ 1 0 9 1 \le x_i, k \le 10^9 1≤xi,k≤109, ∣ s i ∣ < 1 0 5 |s_i| < 10^5 ∣si∣<105。
对于第 1 , 2 1, 2 1,2 组测试数据,保证 n ≤ 10 n\le 10 n≤10。
对于第 3 , 4 , 5 3, 4, 5 3,4,5 组测试数据,保证 n ≤ 500 n \le 500 n≤500。
对于第 6 , 7 , 8 6, 7, 8 6,7,8 组测试数据,保证 d = 1 d = 1 d=1。
最优解是使用单调队列优化的 (但我没用awa)
经过上面的步骤,我们就把这个题转化为了当前mid是否可行。而对此的判断就需要dp求解。
用 f [ i ] f[i] f[i]表示到达 i 点的分数最大值(当前最优是由之前的最优推过来的),而对于当前mid,它能够到达的区间为
[ m a x ( d − m i d , 1 ) , d + m i d ] [max(d-mid,1),d+mid] [max(d−mid,1),d+mid]
所以当前点 f [ i ] f[i] f[i]由前 i − 1 i-1 i−1个点推来 ( x [ j ] − x [ i ] > = 左边界 & & x [ j ] − x [ i ] < = 右边界 ) (x[j]-x[i]>=左边界\&\&x[j]-x[i]<=右边界) (x[j]−x[i]>=左边界&&x[j]−x[i]<=右边界)
x [ i ] x[i] x[i]单调递增,因此反向枚举是更有效的(若当前 x [ j ] − x [ i ] > 右边界 x[j]-x[i]>右边界 x[j]−x[i]>右边界说明之后的点也会超过右边界,直接break)
得到方程
f [ i ] = m a x ( f [ i ] , f [ j ] + s [ i ] ) f[i]=max(f[i],f[j]+s[i]) \space f[i]=max(f[i],f[j]+s[i])
f 数组的初始值应设置为 − I N F -INF −INF(跳不到),f [ 0 ] = 0。
#include
using namespace std;
const int N=1e6+2233;
const int M=5*1e5+23;
#define int long long int
int n,d,k,sum=0;
struct node{
int x,s;
}a[N];
int f[M];
bool check(int mid){
int ls=d-mid,rs=d+mid;
if(ls<=0)ls=1;
memset(f,-0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=n;i++){
for(int j=i-1;j>=0;j--){
if(a[i].x-a[j].x>rs){
break;
}
else if(a[i].x-a[j].x<ls)continue;
f[i]=max(f[i],f[j]+a[i].s);
if(f[i]>=k)return 1;
}
}
return 0;
}
signed main(){
scanf("%lld%lld%lld",&n,&d,&k);
for(int i=1;i<=n;i++){
scanf("%lld %lld",&a[i].x,&a[i].s);
if(a[i].s>0)sum+=a[i].s;
}
if(sum<k){
printf("-1\n");
return 0;
}
int l=0,r=11451;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
r=mid-1;
}
else {
l=mid+1;
}
}
cout<<l<<endl;
return 0;
}