传送门
其实这道题只要想清楚
dp循环变量的意义
以及dp所求的值
就非常简单了
解法:
设\(i\)点获得的分为\(S_i\)
先来考虑一个方面的问题
若我们知道\(g\) 即机器人性能的改变值
怎么求获得的最多的分
可以想到\(dp\)
设\(dp[i]\)表示到i点为止可以获得的最大分
状态转移方程即为 \(dp[i]=max_{j=0->i-1}\{dp[j]\}+S_i\ \text{条件:}(d-g\le X_i-X_j\le d+g)\)
(从0开始是因为可以从一开始位置就直接跳到\(i\)点)
p.s.可以发现其实并不需要枚举j,用单调队列维护即可。
这样我们就找到了知道\(g\)的情况下求获得最大分的方法
接下来就用2分找\(g\)就可以了
当然题目未给上界(有是有,但是为\(10^9\))
所以我们应先用倍增来求\(g\)的区间
代码:
#include
#include
#include
#include
#include
#include
#define inf 2000000000
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
int l,r;
int n,d,k,x[500010],s[500010];
ll dp[500010],sum=0;
int solve(int g)
{
memset(dp,-127,sizeof(dp));
int lp=max(1,d-g),rp=d+g;
ll ans=-inf;
dp[0]=0;
rep(i,1,n)
{
dwn(j,i-1,0)
{
if(x[i]-x[j]>rp) break;
if(x[i]-x[j]=k) return ans;
}
return ans;
}
int main()
{
scanf("%d%d%d",&n,&d,&k);
rep(i,1,n)
{
scanf("%d%d",&x[i],&s[i]);
if(s[i]>0)sum+=s[i];
}
if(sum=k) break;
r*=10;
}
l=r/10;
while(l>1;
if(solve(mid)>=k)
r=mid;
else
l=mid+1;
}
printf("%d\n",l);
return 0;
}
单调队列优化:
int q[500010],ql,qr;
int solve(int g)
{
memset(dp,-127,sizeof(dp));
int lp=max(1,d-g),rp=d+g,cnt=0;
dp[0]=0;
ql=1,qr=0;
rep(i,1,n)
{
while(true)
{
while(ql<=qr&&cnt=lp&&dp[cnt]>=dp[q[qr]]) qr--;
if(cnt=lp) q[++qr]=cnt++;
else break;
}
while(ql<=qr&&x[i]-x[q[ql]]>rp) ql++;
if(ql<=qr) dp[i]=dp[q[ql]]+s[i];
if(dp[i]>=k) return dp[i];
}
return -inf;
}
但是由于这道题枚举时边界的特殊性
用单调队列优化并不能快多少
所以当做练练手写写吧