3/5 keep fighting!! B题的二分答案的出口要搞清。。
A题:
按非降序给出N(2<=N<=15000)个范围在[1, 1e9]的整数,组成集合A,询问M(1<=M<=1e6)次,每次给出一个数,问该数属于集合A的次数。
已知给出的N个数为非降序,在M次查询时,可以使用二分法进行查询,二分法可以用STL中的set实现。
B题:
给定N天每天花的钱数,连续的一段日子可以组成一个月,问将N天组成M个月时,每个月的最少花费是多少。
这是一个最小值最大化的问题,由于问题的答案落在一个区间内:左界是N天里的单天花费的最大值,右界是N天的花费总和,在检验了区间里的某个值的正确性后,就可以确定该点到右界的区间内没有最优解,满足二分答案的思路。
检验某一值x的正确性时,可以采用贪心的策略,当连续几天的花费大于X时,counter++,最后如果counter大于M,即可以x为最大花费时分组可以分成M个月或以上,则x为可行解,否则为不可行解。要关注二分的输出结果情况
#include <iostream>
#include <cstdio>
using namespace std;
int N, M;
const int MAXN = 100000 + 5;
int m[MAXN];
long long L, R, mid;
bool test(int value)
{
int counter = 1;
long long sum = 0;
for (int i = 0; i < N; i++) {
if (sum + m[i] > value) {
sum = m[i];
counter++;
}
else sum += m[i];
}
if (counter > M) return true;
return false;
}
int main()
{
cin >> N >> M;
L = 0;
long long sum = 0;
for (int i = 0; i < N; i++) {
scanf("%d", &m[i]);
if (L < m[i]) L = m[i];
sum += m[i];
}
R = sum;
while (L <= R) {
mid = (L + R) / 2;
if (test(mid)) L = mid + 1;
else R = mid - 1;
}
cout << mid << endl;
}
C题
有一本高为H的书,一个长L,两端高为h的书架,书直立在书架的左端,人从书的底端开始拉书,书开始缓缓倒入书架里,问这个过程中,书的重心偏离书架左端的最远距离
令H与L的夹角为θ,由相似三角形关系得:重心到左侧的d值为
d = cos(θ)*H/2 - h/tan(θ);
由题意可得该过程的初始状态是书紧贴左侧(θ= PI),此时的d值为0, 结束状态如上图所示:当重心H/2恰好在h之上时,此时的d值也为0,此后重心在h的右侧,d值为负数不用考虑。那么在θ=PI到arcsin(2*h/H)的过程中,是一个先增后减的过程,可以用三分法找到峰值。
D题
人开车沿x轴从左向右行使,给定两个点的坐标(x1, y1), (x2, y2) [-1000, 1000] , 求x轴上的点与这两点能够组成的最大角的弧度数。
情况一: 两个坐标点Y值相同,组成的线段与x轴平行,此时线段的延长线与x轴无交点,那么在x取[-1e4, 1e4]过程中,角ABC是一个增加后减小的过程,那么对[-1e4, 1e4]过程进行三分即可得到极大值
情况二:两个坐标点Y值不同但同号,此时线段的延长线与x轴有交点(X0, 0),当B点在(X0, 0)处时,易得角ABC=0 。因此X从[-1e4, X0] , [X0, 1e4]这两个过程中角ABC是一个增加后减小的过程, 用三分法求[-1e4, X0] , [X0, 1e4]这两个区间的极大值后,较大者为结果
情况三:两坐标点Y值异号时,即线段穿过x轴,当B点在(X0, 0)处时,易得角ABC大小为PI
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define PI acos(-1)
#define eps 1e-8
#define LBOUND -1e4
#define RBOUND 1e4
double X1, X2, Y1, Y2;
double X0, answer;
double calcu(double x)
{
double a = x*x - x*X1 - x*X2 + X1*X2 + Y1*Y2;
double b = sqrt(Y1*Y1 + pow((x-X1), 2));
double c = sqrt(Y2*Y2 + pow((x-X2), 2));
return acos(a / (b * c));
}
void solve(double L, double R)
{
double Lmid, Rmid;
while (L + eps < R) {
Lmid = (L * 2 + R) / 3;
Rmid = (L + R * 2) / 3;
if (calcu(Lmid) >= calcu(Rmid)) R = Rmid;
else L = Lmid;
}
if (calcu(L) > answer) answer = calcu(L);
}
int main()
{
cin >> X1 >> Y1 >> X2 >> Y2;
if (Y1 != Y2) {
X0 = X1 - Y1*((X1-X2+0.0) / (Y1-Y2));
if (Y1 * Y2 <= 0) answer = PI;
solve(LBOUND, X0);
solve(X0,RBOUND);
}
else solve(LBOUND, RBOUND);
printf("%.6lf\n", answer);
}