洛谷传送门
BZOJ传送门
题目描述
小 H 发誓要做 21 21 21 世纪最伟大的数学家。他认为,做数学家与做歌星一样,第一步要作好包装,不然本事再大也推不出去。为此他决定先在自己的住所上下功夫,让人一看就知道里面住着一个“未来的大数学家”。
为了描述方便,我们以向东为 x x x 轴正方向,向北为 y y y 轴正方向,建立平面直角坐标系。小 H 的小屋东西长为 100 100 100Hil(Hil 是小 H 自己使用的长度单位,至于怎样折合成 m m m,谁也不知道)。东墙和西墙均平行于 y y y 轴,北墙和南墙分别是斜率为 k 1 k_1 k1 和 k 2 k_2 k2 的直线, k 1 k_1 k1 和 k 2 k_2 k2 为正实数。北墙和南墙的墙角处有很多块草坪,每块草坪都是一个矩形,矩形的每条边都平行于坐标轴。相邻两块草坪的接触点恰好在墙上,接触点的横坐标被称为它所在墙的“分点”,这些分点必须是 1 1 1 到 99 99 99 的整数。
小 H 认为,对称与不对称性的结合才能充分体现“数学美”。因此,在北墙角要有 m m m 块草坪,在南墙角要有 n n n 块草坪,并约定 m ≤ n m \leq n m≤n。如果记北墙和南墙的分点集合分别为 X 1 X_1 X1, X 2 X_2 X2,则应满足 X 1 ⊆ X 2 X_1 \subseteq X_2 X1⊆X2,即北墙的任何一个分点一定是南墙的分点。
由于小 H 目前还没有丰厚的收入,他必须把草坪的造价降到最低,即草坪 的占地总面积最小。你能编程帮他解决这个难题吗?
输入输出格式
输入格式:
仅一行,包含 4 4 4 个数 k 1 k_1 k1, k 2 k_2 k2, m m m, n n n。 k 1 k_1 k1 和 k 2 k_2 k2 为正实数,分别表示北墙和南墙的斜率,精确到小数点后第一位。 m m m 和 n n n 为正整数,分别表示北墙角和南墙角的草坪的块数。
输出格式:
一个实数,表示草坪的最小占地总面积。精确到小数点后第一位。
输入输出样例
输入样例#1:
0.5 0.2 2 4
输出样例#1:
3000.0
说明
【约定】
- 2 ≤ m ≤ n ≤ 100 2 \leq m \leq n \leq 100 2≤m≤n≤100
- 南北墙距离很远,不会出现南墙草坪和北墙草坪重叠的情况
解题分析
贪心地想, 想要最小化面积, 肯定就要均分上下的边长。 所以我们可以先将北墙划分为两个部分: 左边的一部分有 m − n m o d m m-n\ mod\ m m−n mod m块, 每块对应的南墙数量为 ⌊ n m ⌋ \lfloor\frac{n}{m}\rfloor ⌊mn⌋, 右边的一部分有 n m o d m n\ mod\ m n mod m块, 每块对应的南墙数量为 ⌊ n m ⌋ + 1 \lfloor\frac{n}{m}\rfloor+1 ⌊mn⌋+1。
然后我们发现 n , m n,m n,m范围很小, 可以直接枚举左边一部分分配到的大小, 暴力 c h e c k check check即可。 当然, 还是需要均分长度的, 按照类似上面的步骤分配即可。 由不等式易证这个面积是单峰的, 所以我们只要 c h e c k check check到一个位置, 其面积比之前大, 就可以 b r e a k break break了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define db double
db k1, k2, ans = 1e25;
int up, down, lef, rig, lblk, rblk;
template <class T> IN T sqr(T a) {return a * a;}
IN db calc(R int blk, R int len, db rat)
{
if(!blk) return 0;
int llen, rlen, lnum, rnum;
llen = len / blk, rlen = llen + 1;//类似划分为两个部分
rnum = len % blk, lnum = blk - rnum;
return sqr(llen) * rat * lnum + sqr(rlen) * rat * rnum;
}
void solve()
{
db buf;
if(!(down % up)) return printf("%.1lf", calc(lef, 100, k1) + calc(lef * lblk, 100, k2)), void();//如果可以除尽直接平均划分就好
R int bd = 100 - rig * rblk;
for (R int arr = lblk * lef; arr <= bd; ++arr)
{
if(ans > (buf = calc(lef, arr, k1) + calc(rig, 100 - arr, k1) + calc(lblk * lef, arr, k2) + calc(rblk * rig, 100 - arr, k2)))
ans = buf;
else break;
}
printf("%.1lf", ans);
}
int main(void)
{
scanf("%lf%lf%d%d", &k1, &k2, &up, &down);
rig = down % up; lef = up - rig;
lblk = down / up, rblk = down / up + 1;
solve();
}