国王游戏
题目链接:ybt高效进阶1-2-4 / luogu P1080
题目大意
这 n 个大臣排成一排,国王站在队伍的最前面。每个大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数(向下取整)。
国王和大臣左右手的数已经给出。
要让得到金币数最多的人得到的金币尽可能少。
然后要你求这个值。
思路
这道题我们考虑贪心,利用邻项交换法。
首先,我们看两个大臣之间位置是否变化对答案产生的影响。
然后可以看出他们两个位置的变化只会是他们得到的金钱数改变,别的人都不改变。(因为乘法符合交换律)
那假设 s i s_i si 为前 i i i 个人左手值的乘积, l a i la_i lai 和 r a i ra_i rai 分别表示人左右手上写的数字。
那我们可以得到这个:
交换之前 | 交换以后 | |
---|---|---|
第 i i i 个人得到的金子 | s i − 1 r a i \dfrac{s_{i-1}}{ra_i} raisi−1 | S i − 1 r a i + 1 \dfrac{S_{i-1}}{ra_{i+1}} rai+1Si−1 |
第 i i i 个人得到的金子 | s i − 1 × l a i r a i + 1 \dfrac{s_{i-1}\times la_i}{ra_{i+1}} rai+1si−1×lai | s i − 1 × l a i + 1 r a i \dfrac{s_{i-1}\times la_{i+1}}{ra_i} raisi−1×lai+1 |
那我们既然别的都不会变化,我们就看这两个式子的变化咯。
那原来是 max ( s i − 1 r a i , s i − 1 × l a i r a i + 1 ) \max(\dfrac{s_{i-1}}{ra_i},\dfrac{s_{i-1}\times la_i}{ra_{i+1}}) max(raisi−1,rai+1si−1×lai),交换之后就变成了 max ( S i − 1 r a i + 1 , s i − 1 × l a i + 1 r a i ) \max(\dfrac{S_{i-1}}{ra_{i+1}},\dfrac{s_{i-1}\times la_{i+1}}{ra_i}) max(rai+1Si−1,raisi−1×lai+1)。
那这些数都是正整数,那都除一个正整数 s i − 1 s_{i-1} si−1,大小的选择还是一样。
那就是 max ( 1 r a i , l a i r a i + 1 ) \max(\dfrac{1}{ra_i},\dfrac{la_i}{ra_{i+1}}) max(rai1,rai+1lai) 和 max ( 1 r a i + 1 , l a i + 1 r a i ) \max(\dfrac{1}{ra_{i+1}},\dfrac{la_{i+1}}{ra_i}) max(rai+11,railai+1)。
同理,我们再都乘一个 r a i × r a i + 1 ra_i\times ra_{i+1} rai×rai+1,把分母弄掉。
那就是 max ( r a i + 1 , l a i × r a i ) \max(ra_{i+1},la_i\times ra_i) max(rai+1,lai×rai) 和 max ( r a i , l a i + 1 × r a i + 1 ) \max(ra_i,la_{i+1}\times ra_{i+1}) max(rai,lai+1×rai+1)。
那因为这些数都是正整数,我们可以得到其实比较
l
a
i
×
r
a
i
la_{i}\times ra_{i}
lai×rai 和
l
a
i
+
1
×
r
a
i
+
1
la_{i+1}\times ra_{i+1}
lai+1×rai+1 就可以了。
为什么呢?
以左边为例,如果
r
a
i
+
1
ra_{i+1}
rai+1 比
l
a
i
×
r
a
i
la_i\times ra_i
lai×rai 大,而且
r
a
i
≤
l
a
i
×
r
a
i
,
r
a
i
+
1
≤
l
a
i
+
1
×
r
a
i
+
1
ra_i\leq la_i\times ra_i,ra_{i+1}\leq la_{i+1}\times ra_{i+1}
rai≤lai×rai,rai+1≤lai+1×rai+1,那还是右边大。
那我们就以左右手乘积为关键字从小到大排序,然后直接算最大值即可。
这道题最烦的就是要高精,而且是高精乘加高精除(还好是一个高精一个低精),但也非常的烦人。
代码
#include<cstdio>
#include<algorithm>
#define mo 10000
using namespace std;
struct gj {
int n;
long long a[10001];
}ans, cheng;
struct sb {
int l, r;
long long xjb;
}a[1001];
int n, kingl, kingr, tmp;
long long addmore;
bool cmp(sb x, sb y) {
return x.xjb < y.xjb;
}
gj operator /(gj x, int y) {
for (int i = x.n - 1; i >= 1; i--) {
x.a[i - 1] += x.a[i] % y * mo;
x.a[i] /= y;
if (!x.a[i] && i == x.n - 1) x.n--;
}
x.a[0] = x.a[0] / y;
return x;
}
gj operator *(gj x, int y) {
addmore = 0;
for (int i = 0; i < x.n; i++) {
x.a[i] *= y;
x.a[i] += addmore;
addmore = x.a[i] / mo;
x.a[i] %= mo;
}
x.a[x.n] += addmore;
while (x.a[x.n]) {
x.n++;
x.a[x.n] += x.a[x.n - 1] / mo;
x.a[x.n - 1] %= mo;
}
return x;
}
bool operator <(gj x, gj y) {
if (x.n != y.n) return x.n < y.n;
for (int i = x.n - 1; i >= 0; i--)
if (x.a[i] != y.a[i])
return x.a[i] < y.a[i];
return 0;
}
int main() {
scanf("%d %d %d", &n, &kingl, &kingr);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &a[i].l, &a[i].r);
a[i].xjb = (1ll * a[i].l) * (1ll * a[i].r);
}
sort(a + 1, a + n + 1, cmp);
cheng.n = 1;
cheng.a[0] = 1ll * kingl;
for (int i = 1; i <= n; i++) {
if (ans < cheng / a[i].r)
ans = cheng / a[i].r;
cheng = cheng * a[i].l;
}
printf("%lld", ans.a[ans.n - 1]);
for (int i = ans.n - 2; i >= 0; i--) {
tmp = mo / 10;
while (tmp > ans.a[i]) {
printf("0");
tmp /= 10;
}
if (ans.a[i]) printf("%lld", ans.a[i]);
}
return 0;
}