本文同步发表在 YangTY’s Blog
打表可做。
给定 K K K, N N N 和 M M M 以及 K K K 个 A i A_i Ai,构造 B i B_i Bi,使得 ∑ B i = M \sum B_i = M ∑Bi=M 且
max i ∣ B i M − A i N ∣ \max_i\left|\frac{B_i}{M} - \frac{A_i}{N}\right| imax∣∣∣∣MBi−NAi∣∣∣∣
最小。
“最大的最小”,二分答案即可。
要让 max i ∣ B i M − A i N ∣ \max_i\left|\frac{B_i}{M} - \frac{A_i}{N}\right| maxi∣∣MBi−NAi∣∣ 最小,即为让 max i ∣ B i N − A i M ∣ \max_i|B_iN - A_iM| maxi∣BiN−AiM∣ 最小,设为 x x x 即可。
然后我们可以得到 ∀ i , ⌈ M A i − x N ⌉ ≤ B i ≤ ⌊ M A i + x N ⌋ \forall i, \left\lceil \frac{MA_i-x}{N}\right\rceil\leq B_i\leq \left\lfloor\frac{MA_i+x}{N}\right\rfloor ∀i,⌈NMAi−x⌉≤Bi≤⌊NMAi+x⌋。这样子 B i B_i Bi 的上界和下界就出来了,设为 L i L_i Li 和 R i R_i Ri。
然后,如果 ∑ L i ≤ M ≤ ∑ R i \sum L_i\le M\le \sum R_i ∑Li≤M≤∑Ri,则答案一定是可以被构造出来的。这样子我们就可以完成对于一个 x x x 的 check。
如何构造这个答案呢?把所有的 B i B_i Bi 设为 L i L_i Li,然后依次上调来补齐余量 M − ∑ L i M - \sum L_i M−∑Li。
这题就做完了。
#include
#include
#include
#define FOR(i, a, b) for (int i = a; i <= b; ++i)
#define DEC(i, a, b) for (int i = a; i >= b; --i)
#define int long long
const int maxn = 1e5 + 5;
int read()
{
int s = 0, x = 0;
char c = getchar();
while (!isdigit(c))
x |= (c == '-'), c = getchar();
while (isdigit(c))
s = s * 10 + c - '0', c = getchar();
return x ? -s : s;
}
int k, a[maxn], n, m;
int L[maxn], R[maxn], suml, sumr;
inline int max(int a, int b) {return a > b ? a : b;}
inline int min(int a, int b) {return a < b ? a : b;}
bool check(int x)
{
memset(L, 0, sizeof L), memset(R, 0, sizeof R);
suml = sumr = 0;
FOR(i, 1, k)
{
L[i] = max(0, (m * a[i] - x + n - 1) / n);
R[i] = (m * a[i] + x) / n;
suml += L[i], sumr += R[i];
}
return suml <= m && m <= sumr;
}
signed main()
{
k = read(), n = read(), m = read();
FOR(i, 1, k) a[i] = read();
int l = 0, r = n * m, x;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid)) x = mid, r = mid - 1;
else l = mid + 1;
}
check(x);
int sumb = suml;
FOR(i, 1, k)
{
int x = min(R[i] - L[i], m - sumb);
sumb += x;
printf("%d ", x + L[i]);
}
return 0;
}
给定 N N N( 3 ≤ N ≤ 2500 3\le N\le 2500 3≤N≤2500),构造 N N N 个正整数 A i A_i Ai 使得:
这个题需要一个引理:对于一系列已经满足条件的 A i A_i Ai,将任意 A i A_i Ai 的倍数加进去,新得到的集合也是满足条件的。
正确性比较显然,不证。所以从 { 6 , 10 , 15 } \{6, 10, 15\} {6,10,15} 出发,就可以构造出所有的合法答案。
#include
int vis[10005];
int main()
{
int n = 0;
scanf("%d", &n);
for (int i = 1; i * 6 <= 10000; ++i) vis[i * 6] = 1;
for (int i = 1; i * 10 <= 10000; ++i) vis[i * 10] = 1;
for (int i = 1; i * 15 <= 10000; ++i) vis[i * 15] = 1;
int cnt = 4;
printf("%d %d %d\n", 6, 10, 15), vis[6] = vis[10] = vis[15] = 0;
for (int i = 6; i <= 10000 && cnt <= n; ++i) if (vis[i]) printf("%d ", i), ++cnt;
return 0;
}