NOIP2017 跳房子题解
题目描述:
太长了不贴了。
解题思路:
不难想到二分g,判断一下,朴素的算法是n^2 * log(n)的。
check()函数:
O(n^2)做法:
f[i]表示到第i个点的最大分数。
f[i]=max(f[j])+s[i] (J满足x[j]+Max>=x[i],x[j]+Min<=x[i])
O(n)的做法:
单调队列优化,
1.用一个指针now记录入栈的结点,当x[i]-x[now]>=Min进入循环,
在循环的过程中去除无法到达的now,把合法的结点加入栈。
2.将满足上述条件的情况下,去除x[pos[l]]+Max<x[i]的情况,
如果l>r表示没有解。赋值为-INF,反之更新。
代码:
#include<bits/stdc++.h> #define ll long long #define R register using namespace std; const int N=5e5+5; int n,d,k,s[N],x[N],ans=0x3f3f3f3f,f[N]; int pos[N]; inline int check(R int g)//二分g { //f[i] 表示 到第i个点 得到分数的最大值 //f[i]=max(f[i],f[j]+s[i]);j满足 x[j]+Max>=x[i] // x[j]+Min<=x[i] memset(pos,0,sizeof(pos)); memset(f,0,sizeof(f)); R int Min=max(1,d-g); R int Max=d+g; R int l=1,r=0,now=0; if(Max<x[1])return 1; for(R int i=1;i<=n;i++) { while(x[i]-x[now]>=Min) { if(f[now]<=-0x3f3f3f3f) { now++; continue; } while(f[now]>=f[pos[r]]&&l<=r)r--; pos[++r]=now; now++; } while(x[pos[l]]+Max<x[i]&&l<=r)l++; if(l<=r) f[i]=f[pos[l]]+s[i]; else f[i]=-0x3f3f3f3f; //printf("%d %d\n",i,f[i]); if(f[i]>=k)return 0; } return 1; } int main(){ R int l=0,r=0; scanf("%d%d%d",&n,&d,&k); for(R int i=1;i<=n;i++) scanf("%d%d",&x[i],&s[i]); r=x[n]; while(l<r) { R int mid=(l+r)/2; if(check(mid)) { l=mid+1; } else { r=mid; ans=min(ans,mid); } } if(ans==0x3f3f3f3f) printf("-1"); else printf("%d",ans); return 0; }