题目
在一个上凸函数上均匀采样得到{f(i)|i=1,2…N},有M次询问,每次询问输入两个整数a,b,求{f(i)-a*i-b|i=1,2…N}的最大值。
输入:第一行,N和M,其中N,M<5e5
然后N行,按顺序输入f(i)
然后M行,每行两个整数 a,b
--------------------------------------------------------------------------------
思路
运用性质:f[i]-a*i-b最大值处的f函数的切线斜率等于a
二分查找f'[i] = diff[i](离散差分数组)中最小的大于等于a的数的坐标i(可能出现连续多个diff[i]相等的情况,取边缘)
f[i]-a*i-b取最小值的坐标在i的+-1的距离内
注意1:i在diff数组两头的情况
注意2:C++中f[i]的下标从0开始,但题目中f(i)下标从1开始
--------------------------------------------------------------------------------
代码
主程序
// 运用性质:f[i]-a*i-b最大值处的f函数的切线斜率等于a
// 二分查找f'[i] = diff[i](离散差分数组)中最小的大于等于a的数的坐标i
// f[i]-a*i-b取最小值的坐标在i的+-1的距离内
// 注意1:i在diff数组两头的情况
// 注意2:C++中f[i]的下标从0开始,但题目中f(i)下标从1开始
#include<cstdio>
const int NMAX = 5e5+5, INF = 0x3f3f3f3f;
int n, m;
int f[NMAX] = {}, diff[NMAX] = {}; // diff: f的差分,长度为n-1, 由于f是上凸函数,diff是递减函数
int binary_search(int a) // 在diff[i]中搜索最小的大于等于a的数的坐标
{
int l = 0, r = n-2, m = (l+r)/2;
if (diff[0] < a)
{
return 0;
}
else if (diff[n-2] > a)
{
return n-2;
}
while (1)
{
if (l == r)
{
return l;
}
else if (r - l == 1 && diff[r] < a && diff[l] >= a)
{
return r;
}
m = (l+r)/2;
if (diff[m] >= a)
{
l = m;
}
else if (diff[m] < a)
{
r = m;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("xlySE18_02.txt", "r", stdin);
#endif
int i = 0, a, b, ans1, ans2, ans3;
scanf("%d%d", &n, &m);
for (i=0; i<n; i++)
{
scanf("%d", f+i);
if (i > 0)
{
diff[i-1] = f[i] - f[i-1]; // 离散差分近似切线斜率
}
}
while (m--)
{
scanf("%d%d", &a, &b);
i = binary_search(a);
if (i == 0)
{
ans1 = f[0] - a - b;
ans2 = f[1] - a*2 - b;
ans3 = f[2] - a*3 - b;
}
else if (i == n-2)
{
ans1 = f[n-1] - a*(n-1+1) - b;
ans2 = f[n-2] - a*(n-2+1) - b;
ans3 = f[n-3] - a*(n-3+1) - b;
}
else
{
ans1 = f[i] - a*(i+1) - b;
ans2 = f[i-1] - a*(i-1+1) - b;
ans3 = f[i+1] - a*(i+1+1) - b;
}
ans1 = ans1 > ans2 ? ans1 : ans2;
ans1 = ans1 > ans3 ? ans1 : ans3;
printf("%d\n", ans1);
}
return 0;
}
对拍程序
// xlySE18_02.cpp的对拍程序,暴力法
#include<cstdio>
#include<algorithm>
const int NMAX = 5e5+5, INF = 0x3f3f3f3f;
int n, m;
int f[NMAX] = {};
int main()
{
#ifndef ONLINE_JUDGE
freopen("xlySE18_02.txt", "r", stdin);
#endif
int i = 0, a, b, ans = -INF;
scanf("%d%d", &n, &m);
for (i=0; i<n; i++)
{
scanf("%d", f+i);
}
while (m--)
{
scanf("%d%d", &a, &b);
ans = -INF;
for (i=0; i<n; i++)
{
ans = std::max(ans, f[i]-a*(i+1)-b);
}
printf("%d\n", ans);
}
return 0;
}