题目链接
题目描述
国王左、右手分别写下两个整数 ;共有
位大臣,第
位大臣左、右手写下
。国王站最前,其余大臣可任意排序。若某位大臣在队列中的相对位置为
,则他获得的金币数为
其中 为队列中大臣的编号顺序。目标是通过调整大臣顺序,使“获得金币最多的大臣”的金币数尽可能小,并输出该最小值。
(本题为简化版, 不参与计算,仅作为题面设定。)
解题思路
- 记前缀乘积
。第
位大臣的金币为
。
- 只需最小化
。对任意相邻两位大臣
,比较先后次序对“二者的最坏值”的影响,可得应按
的升序排序(交换论证标准结论)。
- 排序后,从前到后维护前缀乘积
,依次计算
的最大值,并在每步将
。
- 为避免溢出:
可能很大,需用“大整数”维护;计算时只涉及“乘以
”与“除以
”。
代码
#include <bits/stdc++.h>
using namespace std;
// 简易大整数(非负),基数 1e9,支持:乘以 int、除以 int(取整商)、比较、输出
struct BigInt {
static const int BASE = 1000000000;
vector<int> d; // 低位在前
BigInt(long long x = 0) { *this = x; }
BigInt& operator=(long long x) {
d.clear();
if (x == 0) return d.push_back(0), *this;
while (x) { d.push_back(int(x % BASE)); x /= BASE; }
return *this;
}
void trim() { while (d.size() > 1 && d.back() == 0) d.pop_back(); }
string toString() const {
if (d.empty()) return "0";
string s = to_string(d.back());
for (int i = (int)d.size() - 2; i >= 0; --i) {
string t = to_string(d[i]);
s += string(9 - t.size(), '0') + t;
}
return s;
}
};
static inline bool operator<(const BigInt& a, const BigInt& b) {
if (a.d.size() != b.d.size()) return a.d.size() < b.d.size();
for (int i = (int)a.d.size() - 1; i >= 0; --i) if (a.d[i] != b.d[i]) return a.d[i] < b.d[i];
return false;
}
static inline bool operator>(const BigInt& a, const BigInt& b) { return b < a; }
static inline void mul_int(BigInt& a, int m) {
if (m == 0) { a = 0; return; }
long long carry = 0;
for (size_t i = 0; i < a.d.size(); ++i) {
long long cur = carry + 1LL * a.d[i] * m;
a.d[i] = int(cur % BigInt::BASE);
carry = cur / BigInt::BASE;
}
while (carry) { a.d.push_back(int(carry % BigInt::BASE)); carry /= BigInt::BASE; }
}
static inline BigInt div_int(const BigInt& a, int v) { // 商
BigInt q; q.d.assign(a.d.size(), 0);
long long rem = 0;
for (int i = (int)a.d.size() - 1; i >= 0; --i) {
long long cur = a.d[i] + rem * BigInt::BASE;
q.d[i] = int(cur / v);
rem = cur % v;
}
q.trim();
return q;
}
struct Node { int l, r; };
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n; if (!(cin >> n)) return 0;
long long L0, R0; cin >> L0 >> R0; // R0 不使用
vector<Node> a(n);
for (int i = 0; i < n; ++i) cin >> a[i].l >> a[i].r;
sort(a.begin(), a.end(), [](const Node& x, const Node& y){
long long X = 1LL * x.l * x.r;
long long Y = 1LL * y.l * y.r;
if (X != Y) return X < Y;
return x.l < y.l; // 次关键字任意,稳定性无关最优性
});
BigInt pref(L0); // 当前前缀乘积 P
BigInt answer(0); // 当前最小化后的“最大金币数”
for (int i = 0; i < n; ++i) {
BigInt gain = div_int(pref, a[i].r); // floor(P / r_i)
if (gain > answer) answer = gain;
mul_int(pref, a[i].l); // P *= l_i
}
cout << answer.toString() << '\n';
return 0;
}
import java.util.*;
import java.math.*;
public class Main {
static class Node { int l, r; Node(int l, int r){ this.l=l; this.r=r; } }
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long L0 = sc.nextLong(), R0 = sc.nextLong(); // R0 不使用
Node[] arr = new Node[n];
for (int i = 0; i < n; i++) arr[i] = new Node(sc.nextInt(), sc.nextInt());
Arrays.sort(arr, (x, y) -> {
long X = 1L * x.l * x.r;
long Y = 1L * y.l * y.r;
if (X != Y) return Long.compare(X, Y);
return Integer.compare(x.l, y.l);
});
BigInteger pref = BigInteger.valueOf(L0);
BigInteger ans = BigInteger.ZERO;
for (Node e : arr) {
BigInteger gain = pref.divide(BigInteger.valueOf(e.r));
if (gain.compareTo(ans) > 0) ans = gain;
pref = pref.multiply(BigInteger.valueOf(e.l));
}
System.out.println(ans.toString());
}
}
n = int(input())
L0, R0 = map(int, input().split()) # R0 不使用
arr = [tuple(map(int, input().split())) for _ in range(n)]
arr.sort(key=lambda x: x[0] * x[1])
pref = L0
ans = 0
for l, r in arr:
gain = pref // r
if gain > ans:
ans = gain
pref *= l
print(ans)
算法及复杂度
- 算法:按
升序排序,线性扫描维护前缀乘积
,在每一步计算
的最大值,最后输出该最大值。
- 时间复杂度:排序
,扫描
;大整数乘除的常数与位数有关,整体可通过本题数据通过。
- 空间复杂度:
(存边与排序,中间使用大整数)。