【地址链接】: 题目地址:https://www.luogu.org/problem/P3957
【思路】:
( 1 ) . (1). (1). 无解情况:所有 ≥ 0 \geq 0 ≥0的分数加起来仍然 < k
( 2 ) . (2). (2). 对于有解情况,我们发现,答案的下界为 0 0 0,答案的上界为 m a x { d , max\{d, max{d,第 n n n个格子的位置 } \} },且答案满足单调性,所以,我们很容易想到二分
( 3 ) . (3). (3).二分花费 m i d mid mid,则跳跃的长度 i i i 满足 m a x ( 1 , d − m i d ) ≤ i ≤ d + m i d max(1,d-mid) \leq i \leq d+mid max(1,d−mid)≤i≤d+mid,考虑如何进行判定,即 c h e c k ( ) check() check() 的实现
( 4 ) . (4). (4).使用动归求,记 f i f_i fi 表示以 i i i格为结尾的分数最大值,我们可以发现 f i = m a x { f j } + i ( m a x ( 1 , d − m i d ) ≤ i − j ≤ d + m i d ) f_i=max \{f_j\}+i(max(1,d-mid) \leq i-j \leq d+mid) fi=max{fj}+i(max(1,d−mid)≤i−j≤d+mid)的分数
( 5 ) . (5). (5). 我们发现普通 d p dp dp的时间复杂度很高,所以我们考虑优化。我们发现区间长度是一定的,所以我们可以使用单调队列进行优化,数据复杂度被降得很低
【代码】:
//By HPXXZYY
#include
using namespace std;
#define gc getchar()
#define g(c) isdigit(c)
inline long long read(){
char c=0;long long x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}//长仅仅6行,但威力无穷的快读
const int N=500100;
struct node{
int sub;long long point;
void read_itself(){
sub=read();point=read();
}
}a[N];long long f[N];
int q[N],le,ri,n,d,k;
const long long inf=0x8080808080808080;
inline bool check(int Left,int Right){
memset(q,0,sizeof(q));
memset(f,0x80,sizeof(f));
// 初始化dp数组和单调队列的过程
le=1;ri=0;int j=0;f[0]=0;
for(int i=1;i<=n;i++){
while (a[i].sub-a[j].sub>=Left&&j<i){
if (f[j]!=inf){
while (le<=ri&&f[q[ri]]<=f[j]) ri--;
// 及时剔除单调队列中不优的答案
q[++ri]=j;//把当前最优答案放入单调队列
}
++j;//别忘了它
}
while (le<=ri&&a[i].sub-a[q[le]].sub>Right) le++;
// 及时把单调队列中过时的答案剔除
if (le<=ri) f[i]=f[q[le]]+a[i].point;
// 利用单调队列中的最优解更新答案
}
for(int i=1;i<=n;i++)
if (f[i]>=k) return true;
return false;
}
int maxn,minn,i;long long sum;
//注意sum一定要是long long类型的
inline int binary_search(int minn,int maxn){
int l=minn,r=maxn,mid,ans;
while (l<=r){
mid=(l+r)>>1;
int minv,maxv;
minv=max(1,d-mid);
maxv=d+mid;
if (check(minv,maxv)){
ans=mid;
r=mid-1;
}
else l=mid+1;
}
return ans;
}//笔者最推荐的二分写法
int main(){
// freopen("t1.in","r",stdin);
n=read();d=read();k=read();
for(i=1;i<=n;i++){
a[i].read_itself();
if (a[i].point>0)
sum+=a[i].point;
// sum:期望最高得分
}
if (sum<k) cout<<-1;
else{
minn=0;maxn=max(d,a[n].sub);
cout<<binary_search(minn,maxn);
}
return 0;
}
安利一下我的洛谷博客:https://www.luogu.org/blog/hpwwzyy2012/