概括

题目 A B C D E F G H I J
进度 \checkmark \checkmark \checkmark \checkmark \checkmark \checkmark \checkmark \checkmark


题解

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(x_1+x_2+...+x_t)^n(x1+x2+...+xt)n 展开后x1n1x2n2...xtntx_1^{n_1}x_2^{n_2}...x_t^{n_t}x1n1x2n2...xtnt 的系数为 <mstyle mathsize="1&#46;2em">n!n1!n2!...nt!</mstyle>\large{\frac{n!}{n_1!n_2!...n_t!}}n1!n2!...nt!n!, 亦可记作 (nn1,n2,...,nt)\binom{n}{n_1,n_2,...,n_t}(n1,n2,...,ntn).

组合意义相当于有 nin_iniiii 号物品,所有物品不同摆放的方案数。(两方案不同当且仅当存在 kkk 使得 kkk 号物品的位置集合不同。)

回到原题,可以利用多项式定理求出所有可能的密码个数 nnn
fff 表示期望的尝试次数,于是有:

f=n1nf+1f = \frac{n-1}{n}f + 1f=nn1f+1

求得 f=nf=nf=n,于是 nnn 就是答案。

#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

假设构造出的字符串有 xxx 个 P 和 yyy 个 R,那么 "RP" 和 "PR" 的总数为 xy=n+mxy = n + mxy=n+m。并且可以通过改变顺序得到任意数量的两者。
枚举 n+mn + mn+m 的因数找到合法的 x,yx, yx,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)O(n)O(n) 筛出来。
然后是一个经典的转化:[l,r][1,r][1,l1][l, r] \to [1, r] - [1, l - 1][l,r][1,r][1,l1],求出哪些数是漂亮数后做个前缀和就能实现 O(1)O(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

首先肯定是将 aia_iai 升序排序,然后问题转化为:

找到一个最长的区间 [l,r][l, r][l,r],满足将该区间中数通过 k\leq kk+1+1+11-11 操作能使得区间所有数相等。求区间 [l,r][l, r][l,r] 的长度。

考虑任意区间 [l,r][l, r][l,r],将该区间所有数变成 xxx 的操作数记作 f(x)f (x)f(x),有

f(x)=<munderover>i=lr</munderover>aixf(x) = \sum_{i=l}^{r} \left| a_i - x \right|f(x)=i=lraix

现在要求 f(x)f(x)f(x) 的最小值。
这也是个经典的模型。结论如下:

k=l+r2k = \frac{l + r}{2}k=2l+r,当 xxxaka_{\lfloor k \rfloor}akaka_{\lceil k \rceil}akf(x)f(x)f(x) 最小。

现在还有一个问题,直接枚举所有区间是不可行的。
但可以对每个 aia_iai,二分求出 x=aix = a_ix=ai 的最长合法区间,再特殊考虑区间长度不为奇数的情况即可。
时间复杂度 O(nlogn)O(n \log n)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;
}