题解 | 小白月赛37(部分)
经此一役小红所向无敌
https://ac.nowcoder.com/acm/contest/11214/A
概括
题目 | A | B | C | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|---|---|---|
进度 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
题解
A
简单模拟。
#include <cstdio>
#include <iostream>
#include <algorithm>
using std::cin;
using std::cout;
using ll = long long;
ll a, h, b, k;
int main() {
std::ios::sync_with_stdio (0), cin.tie (0);
cin >> a >> h >> b >> k;
ll rd = std::min ((h + b - 1) / b, (k + a - 1) / a);
bool f1 = (rd == (h + b - 1) / b), f2 = (rd == (k + a - 1) / a);
ll ans = 0;
ans += (a + b) * rd;
if (!f1) ans += a * 10;
if (!f2) ans += b * 10;
cout << ans << "\n";
return 0;
}
B
首先要知道有个东西叫做 多项式定理:
(x1+x2+...+xt)n 展开后x1n1x2n2...xtnt 的系数为 n1!n2!...nt!n!, 亦可记作 (n1,n2,...,ntn).
组合意义相当于有 ni 个 i 号物品,所有物品不同摆放的方案数。(两方案不同当且仅当存在 k 使得 k 号物品的位置集合不同。)
回到原题,可以利用多项式定理求出所有可能的密码个数 n。
设 f 表示期望的尝试次数,于是有:
求得 f=n,于是 n 就是答案。
#include <cstdio>
#include <iostream>
#include <algorithm>
using std::cin;
using std::cout;
using ll = long long;
const int N = 1e6;
const ll MOD = 1e9 + 7;
ll Fac[N * 10 + 5];
int A[10 + 5];
ll Inv (ll);
int main() {
std::ios::sync_with_stdio (0), cin.tie (0);
int sum = 0, maxa = 0;
for (int i = 1; i <= 10; ++i)
cin >> A[i], sum += A[i], maxa = std::max (maxa, A[i]);
Fac[0] = 1;
for (int i = 1; i <= sum; ++i) Fac[i] = Fac[i - 1] * i % MOD;
ll tap = 1;
for (int i = 1; i <= 10; ++i) if (A[i]) tap = tap * Fac[A[i]] % MOD;
cout << Fac[sum] * Inv (tap) % MOD << "\n";
return 0;
}ll Inv (ll p) {
ll ret = 1, base = p, k = MOD - 2;
while (k) {
if (k & 1) ret = ret * base % MOD;
base = base * base % MOD, k >>= 1;
}return ret;
}
D
前缀和的简单应用。
#include <cstdio>
#include <iostream>
#include <algorithm>
using std::cin;
using std::cout;
using ll = long long;
const int N = 1e5;
int n, k;
ll Sa[N + 5], Sb[N + 5];
int main() {
std::ios::sync_with_stdio (0), cin.tie (0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) {
int tap;
cin >> tap, Sa[i] = Sa[i - 1] + tap;
}for (int i = 1; i <= n; ++i) {
int tap;
cin >> tap, Sb[i] = Sb[i - 1] + tap;
}int pos; ll maxa = 0, minb;
for (int i = 1; i <= n; ++i) {
int r = std::min (i + k - 1, n);
ll ta = Sa[r] - Sa[i - 1], tb = Sb[r] - Sb[i - 1];
if (ta > maxa || ta == maxa && tb < minb) maxa = ta, minb = tb, pos = i;
}cout << pos << "\n";
return 0;
}
E
假设构造出的字符串有 x 个 P 和 y 个 R,那么 "RP" 和 "PR" 的总数为 xy=n+m。并且可以通过改变顺序得到任意数量的两者。
枚举 n+m 的因数找到合法的 x,y 然后构造即可。
#include <cstdio>
using ll = long long;
const int SIZE = 1e5;
ll n, m;
int main() {
scanf ("%lld%lld", &n, &m);
int x, y; // x: "P" y: "R"
for (ll i = 1; i * i <= n + m; ++i) if (!((n + m) % i)) x = i;
y = (n + m) / x;
if (x + y > SIZE) return printf ("-1\n"), 0;
int k = n / x, b = n % x;
for (int i = 1; i <= k; ++i) printf ("R"), --y;
for (int i = 1; i <= x; ++i) {
if (x - i + 1 == b && b) printf ("R"), --y;
printf ("P");
}for (int i = 1; i <= y; ++i) printf ("R");
return 0;
}
F
是博弈论,但不完全是。应该属于找规律类型的。
手玩一下,发现答案似乎与总格子数的奇偶性有关。(证明鸽了)然后结合这题过了一车人的现实,交了一发就过了。
#include <cstdio>
#include <iostream>
using std::cin;
using std::cout;
int main() {
std::ios::sync_with_stdio (0), cin.tie (0);
int n, m;
cin >> n >> m;
cout << (((n * m) & 1) ? "akai" : "yukari") << "\n";
return 0;
}
H
漂亮数可以通过线性筛 O(n) 筛出来。
然后是一个经典的转化:[l,r]→[1,r]−[1,l−1],求出哪些数是漂亮数后做个前缀和就能实现 O(1) 查询啦。
#include <cstdio>
#include <iostream>
using std::cin;
using std::cout;
const int N = 1e8;
const int PN = 1e7;
bool Vis[N + 5];
int Pri[PN + 5], pn;
int S[N + 5];
int test;
int l, r;
void Init();
int main() {
std::ios::sync_with_stdio (0), cin.tie (0);
Init();
cin >> test;
while (test--)
cin >> l >> r, cout << S[r] - S[l - 1] << "\n";
return 0;
}void Init() {
for (int i = 2; i <= N; ++i) {
if (!Vis[i]) Pri[++pn] = i;
for (int j = 1; j <= pn && Pri[j] * i <= N; ++j) {
Vis[Pri[j] * i] = true;
if (!Vis[i]) ++S[Pri[j] * i];
if (!(i % Pri[j])) break;
}S[i] += S[i - 1];
}
}
I
首先肯定是将 ai 升序排序,然后问题转化为:
找到一个最长的区间 [l,r],满足将该区间中数通过 ≤k 次 +1 或 −1 操作能使得区间所有数相等。求区间 [l,r] 的长度。
考虑任意区间 [l,r],将该区间所有数变成 x 的操作数记作 f(x),有
现在要求 f(x) 的最小值。
这也是个经典的模型。结论如下:
设 k=2l+r,当 x 取 a⌊k⌋ 或 a⌈k⌉ 时 f(x) 最小。
现在还有一个问题,直接枚举所有区间是不可行的。
但可以对每个 ai,二分求出 x=ai 的最长合法区间,再特殊考虑区间长度不为奇数的情况即可。
时间复杂度 O(nlogn)。
#include <cstdio>
#include <iostream>
#include <algorithm>
using std::cin;
using std::cout;
using ll = long long;
const int N = 1e5;
int A[N + 5], n;
ll S[N + 5], k;
ll Calc (int, int, int);
int main() {
std::ios::sync_with_stdio (0), cin.tie (0);
cin >> n >> k;
for (int i = 1; i <= n; ++i) cin >> A[i];
std::sort (A + 1, A + 1 + n);
for (int i = 1; i <= n; ++i) S[i] = S[i - 1] + A[i];
int ans = 0;
for (int i = 1; i <= n; ++i) {
int l = 1, r = std::min (i, n - i + 1), fin;
ll res;
while (l <= r) {
int mid = (l + r) >> 1;
ll tap = Calc (i - mid + 1, i, i) + Calc (i, i + mid - 1, i);
if (tap <= k) l = mid + 1, fin = mid, res = k - tap;
else r = mid - 1;
}ans = std::max (ans, fin * 2 - 1);
int ll = i - fin, rr = i + fin;
if (ll >= 1 && A[i] - A[ll] <= res) ans = std::max (ans, fin * 2);
if (rr <= n && A[rr] - A[i] <= res) ans = std::max (ans, fin * 2);
}cout << ans << "\n";
return 0;
}ll Calc (int l, int r, int p) {
ll a = 1ll * A[p] * (r - l + 1), b = S[r] - S[l - 1];
if (r <= p) return a - b;
return b - a;
}
J
简单模拟。
#include <cstdio>
#include <iostream>
#include <cstring>
using std::cin;
using std::cout;
char S[1000 + 200];
int main() {
std::ios::sync_with_stdio (0), cin.tie (0);
cin.getline (S, 1100);
int l = strlen (S);
for (int i = 0; i < l - 3; ++i) cout << S[i];
return 0;
}