CodeTon Round #2

A Two 0-1 sequences

第一个串后 \(m - 1\) 位必须和第二个串一样,前面的位只要有一个第二个串的第一个字符就行。

后来加的题,没代码。

B Luke is a foodie

直接顺着扫到第一个不存在合法的 \(v\) 的区间给答案 +1,然后从这里再开始做就行。

Code
#include <cstdio>
#include <iostream>

using namespace std;

int n, d;

void solve() {
  cin >> n >> d;
  int ans = 0, l = 0, r = 2e9;
  while (n--) {
    int x;
    cin >> x;
    int a = x - d, b = x + d;
    if (b < l || a > r) {
      ans++;
      l = a, r = b;
    } else
      l = max(l, a), r = min(r, b);
  }
  cout << ans << '\n';
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;
  while (t--)
    solve();
}

C Virus

把所有未被感染的连续段拉出来,一定是从长的开始,堵上两边,模拟就行。

Code
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

const int N = 100005;
int n, m, a[N], b[N];

void solve() {
  cin >> n >> m;
  for (int i = 1; i <= m; i++)
    cin >> a[i];
  sort(a + 1, a + m + 1);
  b[1] = a[1] - 1 + n - a[m];
  for (int i = 2; i <= m; i++)
    b[i] = a[i] - a[i - 1] - 1;
  sort(b + 1, b + m + 1, greater<int>());
  int sum = 0;
  for (int i = 1; i <= m; i++) {
    b[i] -= 4 * (i - 1);
    if (b[i] == 1)
      sum += 1;
    if (b[i] > 1)
      sum += b[i] - 1;
  }
  cout << n - sum << '\n';
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;
  while (t--)
    solve();
}

D Magical Array

感觉挺妙的,俺痛 4min 就切了,感觉很牛逼。我想了很长时间(至少 20min),智商不太够。

发现对于 \(s = \sum\limits_{i = 1}^n ia_i\),操作一不会改变 \(s\),而操作二会使 \(s\) 加上 \(1\),把每个数组的 \(s\) 算一下就行。

Code
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

int n, m;

void solve() {
  cin >> n >> m;
  long long mn = 1ll << 60, mx = 0;
  int p = 0;
  for (int i = 1; i <= n; i++) {
    long long s = 0;
    for (int j = 1; j <= m; j++) {
      long long x;
      cin >> x;
      s += x * j;
    }
    if (s < mn)
      mn = s;
    if (s > mx)
      mx = s, p = i;
  }
  cout << p << ' ' << mx - mn << '\n';
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;
  while (t--)
    solve();
}

E Count Seconds

先跑遍拓扑,然后发现 \(n\) 轮之后如果一个点有,那么它能到达的所有点一定有。所以暴力模拟前 \(n\) 轮,然后算出最后到达汇点的值就行。

Code
#include <cstdio>
#include <iostream>
#include <vector>

using namespace std;

const int N = 1005, mod = 998244353;
int n, m, a[N], in[N];
bool u[N];
vector<int> to[N];

int add(int x, int y) { return x + y < mod ? x + y : x + y - mod; }

bool check() {
  for (int i = 1; i <= n; i++)
    if (a[i] > 0)
      return 1;
  return 0;
}

void solve() {
  static int q[N];
  cin >> n >> m;
  for (int i = 1; i <= n; i++)
    cin >> a[i];
  while (m--) {
    int x, y;
    cin >> x >> y;
    to[x].push_back(y);
    in[y]++;
  }

  int t = 0;
  while ((++t) <= n) {
    for (int i = 1; i <= n; i++)
      if (in[i] == 0 && u[i] == 0) {
        u[i] = 1, q[t] = i;
        for (int j : to[i])
          in[j]--;
        break;
      }
  }
  for (int i = 1; i <= n; i++)
    u[i] = 0;

  int ans = 0;
  while (ans <= n && check()) {
    ans++;
    for (int i = n; i; i--)
      if (a[q[i]]) {
        a[q[i]]--;
        for (int j : to[q[i]])
          a[j]++;
      }
  }
  if (!check()) {
    cout << ans << '\n';
    return;
  }

  for (int i = 1; i <= n; i++) {
    a[q[i]] %= mod;
    for (int j : to[q[i]])
      a[j] = add(a[j], a[q[i]]);
  }
  cout << add(ans, a[q[n]]) << '\n';
}

void clear() {
  for (int i = 1; i <= n; i++)
    to[i].clear();
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

  int t;
  cin >> t;
  while (t--) {
    solve();
    clear();
  }
}

F Colouring Game

也挺妙的,完全不会了。

可以发现如果颜色个数不相等,那么谁的多谁就赢,因为刚开始一定是双方将一个 RB 或 BR 删掉,然后没了之后再每次删掉自己的一个颜色,那一定是个数少的人先寄。

不然两个人个数相等,就是比每次删一个 RB 或 BR,看谁先不能操作,就只能删自己的一个,然后寄了。

把每个 RB 相间的段提出来,因为删的是两个不同颜色,所以双方操作集合相同,是个公平博弈。直接对所有长度算出所有 sg 就行。

暴力 sg 是 \(O(n^2)\) 的,但是打表之后能发现一个有个在三十左右的 \(len\),sg 在 \(3 \times len\) 及以后有长为 \(len\) 的循环节,所以打前面一些就行。

这份代码是寄的,因为没有对 \(3 \times len\)\(\min\),但之前数据太水了过了,懒得改了。

Code
#include <cstdio>
#include <iostream>

using namespace std;

int n, f[103];
char a[500005];

void prep() {
  for (int i = 2; i <= 102; i++) {
    bool u[10] = {0};
    for (int j = 1; j != i; j++)
      u[f[j - 1] ^ f[i - j - 1]] = 1;
    for (int x = 0;; x++)
      if (!u[x]) {
        f[i] = x;
        break;
      }
  }
}

void solve() {
  cin >> n >> (a + 1);
  int s = 0, ans = 0;
  for (int i = 1, j; i <= n; i = j + 1) {
    for (j = i; j < n && a[j] != a[j + 1]; j++)
      ;
    ans ^= f[j - i + 1];
  }
  for (int i = 1; i <= n; i++)
    if (a[i] == 'R')
      s++;
    else
      s--;
  if (s > 0 || (s == 0 && ans))
    cout << "Alice\n";
  else
    cout << "Bob\n";
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

  prep();

  int t;
  cin >> t;
  while (t--)
    solve();
}

G Mio and Lucky Array

忽略一些细节,只讲大致思路。

\(a\)\(b\) 的偶数位取反,于是操作变成根据操作开始位置的奇偶性给后面加或减 \(1, 2, \cdots\)。然后做二阶差分,枚举一个开始位置,就要求从这里之后 \(a\)\(b\) 对位相差不超过一,且根据奇偶性是 \(a_i \le b_j \le a_i + 1\)\(b_j \le a_i \le b_j + 1\)。这个是类似经典的 fft 做字符串匹配,可能要设计一个权值,不然平方的话值大于 \(998244353\) 可能在模意义下被判为合法。

还有差分之前的两位是要特殊判的,大概就是开始位置 \(i\) 前面操作的带符号和。\(i + 1\) 位和 \(i\) 的差告诉了 奇数位操作次数 - 偶数位操作次数,而 \(i\) 告诉了 奇数位操作位置和 - 偶数位操作位置和。由于操作是连续的,也就是说是 \(1, 2, 3, 4, \cdots, i\) 都可以操作,所以最后确定了操作次数的差,就可以贪心算出在这个差下 \(a_i\) 可能的最小值和最大值,而在这之间奇偶性相同应该都可以取到,判一下 \(a_i\) 是否在这个区间里即可。

这题是后来加的,所以口胡,没代码。

H Game of AI

建立一张有向图,\(i\) 指向 \(a_i\),那么就得到了一个基环树森林。考虑计算这个基环树森林的答案,那么对于环之外的每个点,如果它没有被孩子占领,那么它的孩子一定会它孩子的孩子占领。于是可以设 \(F_0(x), F_1(x)\) 分别表示自己被孩子占领/不被孩子占领的树的生成函数(EGF),那么则有:

\[F_0(x) = x(F_0(x) + F_1(x)) \exp (F_0(x) + F_1(x)) \tag1 \]

\[F_1(x) = x \exp F_0(x) \tag 2 \]

\(F_0(x)\) 递推即为确定被哪个孩子占领,\(F_1(x)\) 就是孩子全部被孩子的孩子占领。

\((2)\) 带入 \((1)\) 即可得到 \(F_0(x)\) 的方程,牛顿迭代即可得到 \(F_0(x)\),进而得到 \(F_1(x)\)

现在考虑把树拼起来变成一棵基环树。我们设在环上的一个点和它的子树,它被占领/不被占领的生成函数为 \(G_0(x), G_1(x)\),那么考虑一个点是被自己的孩子占领还是被环上的上一个点占领,生成函数就是 \(G_0(x) = F_0(x) + F_1(x)\)

考虑环上每一个点,如果某个点没有被别人占领那么它在环上的后一个点一定会被占领。那么如果一个点它没有被别的点占领,我们就在后面强制拼上一个被占领的点,即 \(G_1(x) = F_1(x)G_0(x)\)

对于环还有一个限制,就是不能环上的每一个点都被它在环上的前一个点占领。可以证明一个环合法当且仅当这两个条件成立。

\(G_2(x) = G_0(x) + G_1(x)\)。先不考虑第二个限制,那么一个环的生成函数就是:

\[\sum\limits_{i = 1}^{+\infty} \frac{G_2(x)^i}{i} - G_0(x) = -\ln(1 - G_2(x)) - G_0(x) \]

减掉 \(G_0(x)\) 是因为环上不能只有一个点。

考虑第二个限制,类似上面可以得到违反第二条限制的生成函数是 \(-\ln(1 - F_1(x)) - F_1(x)\)

两部分相减得到基环树的生成函数之后再 \(\exp\) 一下就能得到原问题(基环树森林)的答案了。

时间复杂度 \(O(n \log n)\)

Code
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

typedef unsigned long long u64;
typedef __uint128_t u128;

const int o = 18, len = 1 << o, B = 16;
int n, fac[len], ifac[len], iv[len], mod;
int f[len], ef[len], xef[len], a[len], b[len], h[len];

struct fastmod {
  u64 b;
  int m;

  fastmod(int mod) : b(((u128)1 << 64) / mod), m(mod) {}
  int reduce(u64 a) {
    u64 q = ((u128)a * b) >> 64;
    int r = a - q * m;
    return r < m ? r : r - m;
  }
} z(998244353);

int add(int x, int y) { return x + y < mod ? x + y : x + y - mod; }
int sub(int x, int y) { return x < y ? x + mod - y : x - y; }

int power(int a, int n) {
  int tp = 1;
  while (n) {
    if (n & 1)
      tp = z.reduce(1ll * tp * a);
    a = z.reduce(1ll * a * a), n >>= 1;
  }
  return tp;
}

void prep(int n) {
  fac[0] = 1;
  for (int i = 1; i <= n; i++)
    fac[i] = z.reduce(1ll * fac[i - 1] * i);
  ifac[n] = power(fac[n], mod - 2);
  for (int i = n - 1; i != -1; i--)
    ifac[i] = z.reduce(1ll * ifac[i + 1] * (i + 1));
  for (int i = 1; i <= n; i++)
    iv[i] = z.reduce(1ll * ifac[i] * fac[i - 1]);
  iv[0] = 1;
}

namespace poly {
int I[len], w[len], r[len], up, l;

int findg(int n) {
  static int a[101];
  int cnt = 0, x = n - 1;
  for (int i = 2; i * i <= x; i++)
    if (x % i == 0) {
      a[++cnt] = i;
      while (x % i == 0)
        x /= i;
    }
  if (x > 1)
    a[++cnt] = x;

  for (int g = 2;; g++) {
    bool ok = 1;
    for (int i = 1; i <= cnt; i++)
      if (power(g, (n - 1) / a[i]) == 1) {
        ok = 0;
        break;
      }
    if (ok)
      return g;
  }
}

void init() {
  I[0] = 1;
  const int w0 = power(findg(mod), (mod - 1) >> o);
  w[len >> 1] = 1;
  for (int i = (len >> 1) + 1; i != len; i++)
    w[i] = z.reduce(1ll * w[i - 1] * w0);
  for (int i = (len >> 1) - 1; i; i--)
    w[i] = w[i << 1];
  for (int i = 0; i != len; i++)
    r[i] = (r[i >> 1] >> 1) | ((i & 1) << (o - 1));
}

void ntt(int *a, int n, bool op) {
  static u64 t[len], x, y;
  for (int i = 0; i != n; i += 2) {
    x = a[r[i] >> (o - l)], y = a[r[i + 1] >> (o - l)];
    t[i] = x + y, t[i + 1] = x + mod - y;
  }
  for (int l = 2; l != n; l <<= 1) {
    int *k = w + l;
    for (u64 *f = t; f != t + n; f += l)
      for (int *j = k; j != k + l; j++, f++) {
        u64 x = *f, y = z.reduce(f[l] * *j);
        f[l] = x + mod - y, *f += y;
      }
  }
  if (op) {
    for (int i = 0, x = mod - (mod >> l); i != n; i++)
      a[i] = z.reduce(t[i] * x);
    reverse(a + 1, a + n);
  } else
    for (int i = 0; i != n; i++)
      a[i] = z.reduce(t[i]);
}

void pre(int n) { l = 32 - __builtin_clz(n), up = 1 << l; }

void mul(int *f, int n, int *g, int m, int *h, int q) {
  static int x[len], y[len];
  memcpy(x, f, (n + 1) << 2), memcpy(y, g, (m + 1) << 2);
  pre(n + m), ntt(x, up, 0), ntt(y, up, 0);
  for (int i = 0; i < up; i++)
    h[i] = z.reduce(1ll * x[i] * y[i]);
  ntt(h, up, 1);
  memset(x, 0, up << 2), memset(y, 0, up << 2), fill(h + q + 1, h + up, 0);
}

void div(int *a, int *b, int n, int *f) {
  static int iv[len], x[len], tmp[len];
  static int nb[len << 2], nf[len << 2];
  static u64 s0[len], s1[len];
  if (n <= 16) {
    int x = power(b[0], mod - 2);
    for (int i = 0; i <= n; i++) {
      u64 s = 0;
      for (int j = 0; j != i; j++)
        s += 1ll * f[j] * b[i - j];
      f[i] = z.reduce(1ll * x * (a[i] + mod - z.reduce(s)));
    }
    return;
  }

  int m = 1 << (32 - __builtin_clz(n));
  int k = m >> 4, z = k << 1;
  div(I, b, k - 1, tmp), memcpy(x, a, k << 2);
  memcpy(iv, tmp, k << 2), memset(tmp, 0, k << 2);
  pre(z - 1);
  ntt(iv, up, 0), ntt(x, up, 0);
  for (int i = 0; i != up; i++)
    x[i] = ::z.reduce(1ll * x[i] * iv[i]);
  ntt(x, up, 1);
  memcpy(f, x, k << 2);
  memset(x, 0, up << 2);
  memcpy(nb, b, k << 2);
  ntt(nb, up, 0);
  for (int i = 1;; i++) {
    if (i * k > n) {
      memset(iv, 0, up << 2);
      memset(nb, 0, i * z * 4);
      memset(nf, 0, (i - 1) * z * 4);
      fill(f + n + 1, f + i * k, 0);
      break;
    }
    memcpy(nb + i * z, b + i * k, k << 2);
    memcpy(nf + (i - 1) * z, f + (i - 1) * k, k << 2);
    ntt(nb + i * z, up, 0), ntt(nf + (i - 1) * z, up, 0);
    for (int l1 = 0; l1 != i; l1++)
      for (int j = 0; j != up; j++)
        s0[j] += 1ll * nf[l1 * z + j] * nb[(i - l1) * z + j];
    for (int l1 = 0; l1 != i; l1++)
      for (int j = 0; j != up; j++)
        s1[j] += 1ll * nf[l1 * z + j] * nb[(i - l1 - 1) * z + j];
    for (int j = 0; j != up; j += 2) {
      x[j] = ::z.reduce(s0[j] + ::z.reduce(s1[j]));
      x[j + 1] = ::z.reduce(s0[j + 1] + mod - ::z.reduce(s1[j + 1]));
      s0[j] = s1[j] = s0[j + 1] = s1[j + 1] = 0;
    }
    ntt(x, up, 1);
    memset(x + k, 0, k << 2);
    for (int j = 0; j != k; j++)
      x[j] = sub(a[i * k + j], x[j]);
    ntt(x, up, 0);
    for (int j = 0; j != up; j++)
      x[j] = ::z.reduce(1ll * x[j] * iv[j]);
    ntt(x, up, 1);
    memcpy(f + i * k, x, k << 2);
    memset(x, 0, up << 2);
  }
}

void dcexp(int *a, int l, int r, int n, int *f, int *g, int *h) {
  static u64 s[len];
  static int tp[len];
  if (r - l + 1 <= 32) {
    for (int i = l; i <= r && i <= n; i++) {
      u64 s = 0;
      for (int j = l; j < i; j++) {
        s += 1ll * f[j] * a[i - j];
        if (!(j & 15))
          s = z.reduce(s);
      }
      f[i] = z.reduce(((u64)f[i] + z.reduce(s)) * iv[i]);
    }
    return;
  }
  int *tg[B], *th[B];
  int len = (r - l + 1) / B, k = 2 * len;
  for (int i = 0; i < B - 1; i++)
    tg[i] = g + i * k, th[i] = h + i * k;
  if (!l) {
    pre(k - 1);
    for (int i = 0; i < B - 1; i++) {
      if ((i + 1) * len > n)
        break;
      memcpy(th[i], a + i * len, k << 2);
      ntt(th[i], k, 0);
    }
  }
  for (int i = 0; i < B; i++) {
    if (l + i * len > n)
      break;
    memset(s, 0, k << 3);
    for (int j = 0; j != i; j++)
      for (int t = 0; t != k; t++)
        s[t] += 1ll * tg[j][t] * th[i - j - 1][t];
    for (int t = 0; t != k; t++)
      tp[t] = z.reduce(s[t]);
    pre(k - 1), ntt(tp, k, 1);
    for (int t = 0; t < len; t++)
      f[l + i * len + t] = add(f[l + i * len + t], tp[t + len]);
    dcexp(a, l + i * len, l + (i + 1) * len - 1, n, f, g + k * B, h + k * B);
    if (i != B - 1) {
      memcpy(tg[i], f + l + i * len, len << 2);
      pre(k - 1), ntt(tg[i], k, 0);
    }
  }
  memset(tg[0], 0, (k * B) << 2);
}

void exp(int *a, int n, int *f) {
  static int x[len << 1], v1[len << 2], v2[len << 2];
  for (int i = 1; i <= n; i++)
    x[i] = z.reduce(1ll * a[i] * i);
  f[0] = 1, fill(f + 1, f + n + 1, 0);
  int m = 1 << (32 - __builtin_clz(n));
  dcexp(x, 0, m - 1, n, f, v1, v2);
  memset(x, 0, (n + 1) << 2), fill(f + n + 1, f + m, 0);
}
} // namespace poly

void calc(int n) {
  static int xef[len], eef[len], a[len], b[len], c[len];
  if (n == 2) {
    f[2] = 1;
    return;
  }

  calc((n + 1) / 2);
  poly::exp(f, n - 1, xef);
  for (int i = n; i; i--)
    xef[i] = xef[i - 1];
  xef[0] = 0;
  for (int i = 1; i <= n; i++)
    a[i] = add(xef[i], f[i]);
  poly::exp(a, n - 1, eef);
  for (int i = n; i; i--)
    eef[i] = eef[i - 1];
  eef[0] = 0;
  poly::pre(2 * n);
  int up = poly::up;
  poly::ntt(a, up, 0), poly::ntt(eef, up, 0);
  for (int i = 0; i != up; i++)
    c[i] = z.reduce(1ll * a[i] * eef[i]);
  poly::ntt(c, up, 1), fill(c + n + 1, c + up, 0);
  for (int i = 1; i <= n; i++)
    c[i] = sub(f[i], c[i]);
  xef[0] = 1;
  poly::ntt(xef, up, 0);
  for (int i = 0; i != up; i++)
    b[i] = z.reduce(1ll * xef[i] * (a[i] + 1));
  poly::ntt(b, up, 1);
  fill(b + n + 1, b + up, 0);
  poly::ntt(b, up, 0);
  for (int i = 0; i != up; i++)
    b[i] = z.reduce(1ll * b[i] * eef[i]);
  poly::ntt(b, up, 1);
  fill(b + n + 1, b + up, 0);
  b[0] = 1;
  for (int i = 1; i <= n; i++)
    b[i] = sub(0, b[i]);
  memset(a, 0, up << 2);
  poly::div(c, b, n, a);
  for (int i = 2; i <= n; i++)
    f[i] = sub(f[i], a[i]);

  memset(xef, 0, up << 2), memset(eef, 0, up << 2);
  memset(a, 0, (n + 1) << 2), memset(b, 0, (n + 1) << 2), memset(c, 0, (n + 1) << 2);
}

int main() {
  ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

  cin >> n >> mod, z = fastmod(mod);
  if (n == 1) {
    cout << 0;
    return 0;
  }
  poly::init(), prep(n);

  calc(n);

  poly::exp(f, n, ef);
  for (int i = n; i; i--)
    a[i] = add(f[i], ef[i - 1]);
  poly::exp(a, n, h);
  for (int i = n; i; i--)
    h[i] = h[i - 1];
  h[0] = 0;
  for (int i = 1; i <= n; i++)
    a[i] = add(f[i], h[i]);
  for (int i = n; i; i--)
    xef[i] = ef[i - 1];
  xef[0] = 1;
  poly::mul(a, n, xef, n, a, n);
  a[0] = 1;
  for (int i = 1; i <= n; i++)
    a[i] = sub(0, a[i]);
  poly::mul(a, n, ef, n, a, n);
  h[0] = 1;
  for (int i = 1; i <= n; i++)
    h[i] = sub(0, h[i]);
  poly::div(h, a, n, b);
  for (int i = 1; i <= n; i++)
    cout << z.reduce(1ll * b[i] * fac[i]) << '\n';
}
posted @ 2022-07-31 22:37  Salley-Garden  阅读(2533)  评论(4编辑  收藏  举报