CodeForces Gym 102129 简要题解

Tritwise Mex

c 0 = a 1 b 2 + a 2 b 1 + a 1 b 1 + a 2 b 2 c_0 = a_1b_2 + a_2b_1 + a_1b_1 + a_2b_2 c0=a1b2+a2b1+a1b1+a2b2

c 1 = a 0 b 2 + a 2 b 0 + a 0 b 0 c_1 = a_0b_2 + a_2b_0 + a_0b_0 c1=a0b2+a2b0+a0b0

c 2 = a 0 b 1 + a 1 b 0 c_2 = a_0b_1 + a_1b_0 c2=a0b1+a1b0

计算 ( a 1 + a 2 ) ( b 1 + b 2 ) , ( a 0 + a 2 ) ( b 0 + b 2 ) , a 2 b 2 , ( a 0 + a 1 + a 2 ) ( b 0 + b 1 + b 2 ) (a_1+a_2)(b_1+b_2), (a_0+a_2)(b_0+b_2), a_2b_2, (a_0+a_1+a_2)(b_0+b_1+b_2) (a1+a2)(b1+b2),(a0+a2)(b0+b2),a2b2,(a0+a1+a2)(b0+b1+b2) ,线性组合求出 c c c ,递归计算即可。

#include <bits/stdc++.h>

using namespace std;

vector<long long> multiply(const vector<int> &a, const vector<int> &b, int n) {
  if (n == 1) {
    return vector<long long>(1, (long long) a[0] * b[0]);
  } else {
    n /= 3;
    vector<int> aa(n), bb(n);
    for (int i = 0; i < n; ++i) {
      aa[i] = a[i + n * 2];
      bb[i] = b[i + n * 2];
    }
    vector<long long> foo = multiply(aa, bb, n);
    for (int i = 0; i < n; ++i) {
      aa[i] += a[i];
      bb[i] += b[i];
    }
    vector<long long> bar = multiply(aa, bb, n);
    for (int i = 0; i < n; ++i) {
      aa[i] += a[i + n];
      bb[i] += b[i + n];
    }
    vector<long long> baz = multiply(aa, bb, n);
    for (int i = 0; i < n; ++i) {
      aa[i] -= a[i];
      bb[i] -= b[i];
    }
    vector<long long> c = multiply(aa, bb, n);
    c.resize(n * 3);
    for (int i = 0; i < n; ++i) {
      c[i + n] = bar[i] - foo[i];
      c[i + n * 2] = baz[i] - c[i] - c[i + n];
    }
    return c;
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  int m = 1;
  for (int i = 0; i < n; ++i) {
    m *= 3;
  }
  vector<int> a(m, 1), b(m, 1);
  for (int i = 0; i < m; ++i) {
    cin >> a[i];
  }
  for (int i = 0; i < m; ++i) {
    cin >> b[i];
  }
  vector<long long> c = multiply(a, b, m);
  for (int i = 0; i < m; ++i) {
    if (i) {
      cout << " ";
    }
    cout << c[i];
  }
  cout << "\n";
  return 0;
}

Associativity Degree

可以发现最后只需要满足某种性质的矩阵即可表示出所有答案,具体可以参见代码。(我咋知道怎么发现的

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<vector<int>> same(19683, vector<int>(27, -1));
  vector<vector<int>> small(3, vector<int>(3));
  vector<vector<int>> with(3, vector<int>(3));
  vector<vector<int>> without(3, vector<int>(3));
  vector<int> to(3);
  auto get = [&](int hash_all, int hash_to) {
    if (same[hash_all][hash_to] == -1) {
      int res = 0;
      for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
          if (to[small[i][j]] == small[i][to[j]]) {
            ++res;
          }
        }
      }
      same[hash_all][hash_to] = res;
    }
    return same[hash_all][hash_to];
  };
  auto solve = [&](int a, int b, int d, int e, int g, int h) {
    for (int i = 0; i < 3; ++i) {
      small[0][i] = i < a ? 0 : i < b ? 1 : 2;
      small[1][i] = i < d ? 0 : i < e ? 2 : 0;
      small[2][i] = i < g ? 0 : i < h ? 2 : 1;
    }
    with[0][0] = a;
    with[0][1] = b - a;
    with[0][2] = n - b;
    with[1][0] = d + n - e;
    with[1][1] = 0;
    with[1][2] = e - d;
    with[2][0] = g;
    with[2][1] = n - h;
    with[2][2] = h - g;
    without = with;
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        --without[i][small[i][j]];
      }
    }
    int res = 0;
    res += with[0][0] * n * (n - 3);
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        res += without[i][j] * with[j][small[i][0]];
      }
    }
    int hash_all = 0;
    for (int i = 0; i < 3; ++i) {
      for (int j = 0; j < 3; ++j) {
        hash_all = hash_all * 3 + small[i][j];  
      }
    }
    vector<int> disc = {0, a, b, d, e, g, h, n};
    sort(disc.begin(), disc.end());
    disc.erase(unique(disc.begin(), disc.end()), disc.end());
    for (int i = 0; i + 1 < (int) disc.size(); ++i) {
      int p = disc[i];
      to[0] = p < a ? 0 : p < b ? 1 : 2;
      to[1] = p < d ? 0 : p < e ? 2 : 0;
      to[2] = p < g ? 0 : p < h ? 2 : 1;
      res += (disc[i + 1] - disc[i]) * get(hash_all, to[0] * 9 + to[1] * 3 + to[2]);
    }
    return res;
  };
  vector<vector<int>> ans(n * n * n + 1);
  if (n >= 3) {
    int need = n * n * n + 1;
    for (int a = 0; a <= n; ++a) {
      for (int b = a; b <= n && min(a, b - a) == 0; ++b) {
        for (int d = 0; d <= 2 - (n >= 20); ++d) {
          for (int e = d; e <= n && min(d, e - d) <= 1; ++e) {
            for (int g = 0; g <= n; ++g) {
              for (int h = g; h <= n; ++h) {
                int c = solve(a, b, d, e, g, h);
                if (ans[c].empty()) {
                  ans[c] = vector<int>{a, b, d, e, g, h};
                  --need;
                  if (!need) {
                    goto found;
                  }
                }
              }
            }
          }
        }
      }
    }
    found:;
  }
  int q;
  cin >> q;
  while (q--) {
    int need;
    cin >> need;
    if (n == 1) {
      if (need == 1) {
        cout << "YES" << "\n";
        cout << 1 << "\n";
      } else {
        cout << "NO" << "\n";
      }
    } else if (n == 2) {
      vector<vector<int>> res(n, vector<int>(n));
      for (int a = 0; a < n; ++a) {
        for (int b = 0; b < n; ++b) {
          for (int c = 0; c < n; ++c) {
            for (int d = 0; d < n; ++d) {
              res[0][0] = a;
              res[0][1] = b;
              res[1][0] = c;
              res[1][1] = d;
              int cnt = 0;
              for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                  for (int k = 0; k < n; ++k) {
                    if (res[res[i][j]][k] == res[i][res[j][k]]) {
                      ++cnt;
                    }
                  }
                }
              }
              if (cnt == need) {
                cout << "YES" << "\n";
                cout << a + 1 << " " << b + 1 << "\n";
                cout << c + 1 << " " << d + 1 << "\n";
                goto outer;
              }
            }
          }
        }
      }
      cout << "NO" << "\n";
      outer:;
    } else {
      vector<vector<int>> res(n, vector<int>(n));
      for (int i = 0; i < n; ++i) {
        res[0][i] = i < ans[need][0] ? 0 : i < ans[need][1] ? 1 : 2;
        res[1][i] = i < ans[need][2] ? 0 : i < ans[need][3] ? 2 : 0;
        res[2][i] = i < ans[need][4] ? 0 : i < ans[need][5] ? 2 : 1;
      }
      cout << "YES" << "\n";
      for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
          if (j) {
            cout << " ";
          }
          cout << res[i][j] + 1;
        }
        cout << "\n";
      }
    }
  }
  return 0;
}

Medium Hadron Collider

题意即为 − ( x + 1 ) a ( x − 1 ) n − a -(x+1)^a(x-1)^{n-a} (x+1)a(x1)na ,求 [ x k ] [x^k] [xk] 系数可以把它表示成一个关于 a a a 的多项式,注意到 x = a x=a x=a 时, P ( a ) = ( P ( x ) &VeryThinSpace; m o d &VeryThinSpace; ( x − a ) ) ( a ) P(a)=(P(x)\bmod (x-a))(a) P(a)=(P(x)mod(xa))(a) ,那么我们求出一堆 P ( x ) − P ( a ) P(x)-P(a) P(x)P(a) 的多项式 gcd ⁡ \gcd gcd 即可解出 a a a

#include <bits/stdc++.h>

using namespace std;

const int md = (int) 2e7 - 1;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (int) ((long long) x * y % md);
}

inline int inv(int a) {
  int b = md, u = 0, v = 1;
  while (a) {
    int t = b / a;
    b -= t * a;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  if (u < 0) {
    u += md;
  }
  return u;
}

vector<int>& operator += (vector<int> &a, const vector<int> &b) {
  if (a.size() < b.size()) {
    a.resize(b.size());
  }
  for (int i = 0; i < (int) b.size(); ++i) {
    add(a[i], b[i]);
  }
  return a;
}

vector<int> operator + (const vector<int> &a, const vector<int> &b) {
  vector<int> c = a;
  return c += b;
}

vector<int>& operator -= (vector<int> &a, const vector<int> &b) {
  if (a.size() < b.size()) {
    a.resize(b.size());
  }
  for (int i = 0; i < (int) b.size(); ++i) {
    sub(a[i], b[i]);
  }
  return a;
}

vector<int> operator - (const vector<int> &a, const vector<int> &b) {
  vector<int> c = a;
  return c -= b;
}

vector<int>& operator *= (vector<int> &a, const vector<int> &b) {
  vector<int> c = a;
  a.assign(a.size() + b.size() - 1, 0);
  for (int i = 0; i < (int) c.size(); ++i) {
    for (int j = 0; j < (int) b.size(); ++j) {
      add(a[i + j], mul(c[i], b[j]));
    }
  }
  return a;
}

vector<int> gcd(vector<int> a, vector<int> b) {
  while (true) {
    while (!a.empty() && a.back() == 0) {
      a.pop_back();
    }
    while (!b.empty() && b.back() == 0) {
      b.pop_back();
    }
    if (a.size() > b.size()) {
      swap(a, b);
    }
    if (a.empty()) {
      return b;
    }
    int coef = mul(b.back(), inv(a.back()));
    for (int i = 0; i < (int) a.size(); ++i) {
      sub(b[b.size() - a.size() + i], mul(a[i], coef));
    }
  }
}

int main() {
  int n;
  cin >> n;
  --n;
  vector<int> fact(n + 1), inv_fact(n + 1);
  fact[0] = fact[1] = inv_fact[0] = inv_fact[1] = 1;
  for (int i = 2; i <= n; ++i) {
    fact[i] = mul(fact[i - 1], i);
    inv_fact[i] = mul(inv_fact[md % i], md - md / i);
  }
  for (int i = 2; i <= n; ++i) {
    inv_fact[i] = mul(inv_fact[i - 1], inv_fact[i]);
  }
  auto binom = [&](int x, int y) {
    return x < y ? 0 : mul(fact[x], mul(inv_fact[y], inv_fact[x - y]));
  };
  auto make_poly = [&](int d) {
    vector<int> res;
    for (int i = 0; i <= d; ++i) {
      vector<int> poly(1, 1);
      for (int j = 0; j < i; ++j) {
        poly *= vector<int>{md - j, 1};
      }
      for (int j = 0; j < d - i; ++j) {
        poly *= vector<int>{n - j, md - 1};
      }
      poly *= vector<int>(1, mul(inv_fact[i], inv_fact[d - i]));
      if ((n + d + i) & 1) {
        res += poly;
      } else {
        res -= poly;
      }
    }
    return res;
  };
  vector<int> pos, neg;
  int a = -1;
  int d = 128;
  while (true) {
    vector<int> poly = make_poly(d);
    cout << "? " << d + 1 << endl;
    int res;
    cin >> res;
    if (res < 0) {
      res += md;
    }
    pos = gcd(pos, poly - vector<int>(1, res));
    neg = gcd(neg, poly + vector<int>(1, res));
    vector<int> candidates;
    auto check = [&](vector<int> v, int parity) {
      if ((int) v.size() != 2) {
        return;
      }
      int a = (md - mul(v[0], inv(v[1]))) % md;
      if ((a & 1) == parity && a <= n) {
        candidates.push_back(a);
      }
    };
    check(pos, 0);
    check(neg, 1);
    if ((int) candidates.size() == 1) {
      a = candidates[0];
      break;
    }
    ++d;
  }
  cout << "!";
  for (int d = 0; d < 128; ++d) {
    int res = 0;
    for (int i = 0; i <= d; ++i) {
      int coef = mul(binom(a, i), binom(n - a, d - i));
      if ((n + d + i + a) & 1) {
        add(res, coef);
      } else {
        sub(res, coef);
      }
    }
    if (res >= (int) 1e7) {
      res -= md;
    }
    cout << " " << res;
  }
  cout << endl;
  return 0;
}

Basis Change

线性递推求出对应系数后高斯消元即可。

#include <bits/stdc++.h>

using namespace std;

const int md = (int) 1e9 + 7;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (int) ((long long) x * y % md);
}

inline int inv(int a) {
  int b = md, u = 0, v = 1;
  while (a) {
    int t = b / a;
    b -= t * a;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  if (u < 0) {
    u += md;
  }
  return u;
}

vector<int>& operator *= (vector<int> &a, vector<int> b) {
  vector<int> c = a;
  a.assign(a.size() + b.size() - 1, 0);
  for (int i = 0; i < (int) c.size(); ++i) {
    for (int j = 0; j < (int) b.size(); ++j) {
      add(a[i + j], mul(c[i], b[j]));
    }
  }
  return a;
}

vector<int>& operator %= (vector<int> &a, const vector<int> &b) {
  while (a.size() >= b.size()) {
    int coef = mul(a.back(), inv(b.back()));
    for (int i = 0; i < (int) b.size(); ++i) {
      sub(a[a.size() - b.size() + i], mul(b[i], coef));
    }
    a.pop_back();
  }
  return a;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<int> a(n + 1);
  a[n] = 1;
  for (int i = n - 1; ~i; --i) {
    cin >> a[i];
    a[i] = (md - a[i]) % md;
  }
  vector<int> b(n);
  for (int i = 0; i < n; ++i) {
    cin >> b[i];
  }
  vector<vector<int>> g(n, vector<int>(n + 1));
  auto add = [&](int foo, int bar) {
    vector<int> res = vector<int>{1};
    vector<int> x = vector<int>{0, 1};
    x %= a;
    while (foo) {
      if (foo & 1) {
        res *= x;
        res %= a;
      }
      x *= x;
      x %= a;
      foo >>= 1;
    }
    res.resize(n);
    for (int i = 0; i < n; ++i) {
      g[i][bar] = res[i];
    }
  };
  for (int i = 0; i < n; ++i) {
    add(b[n - 1] - b[i], i);
  }
  add(b[n - 1], n);
  for (int i = 0; i < n; ++i) {
    int p = i;
    while (!g[p][i]) {
      ++p;
    }
    if (p != i) {
      swap(g[i], g[p]);
    }
    int coef = inv(g[i][i]);
    for (int j = i; j <= n; ++j) {
      g[i][j] = mul(g[i][j], coef);
    }
    for (int j = 0; j < n; ++j) {
      if (j != i) {
        int coef = g[j][i];
        for (int k = i; k <= n; ++k) {
          sub(g[j][k], mul(g[i][k], coef));
        }
      }
    }
  }
  for (int i = 0; i < n; ++i) {
    if (i) {
      cout << " ";
    }
    cout << g[i][n];
  }
  cout << "\n";
  return 0;
}

Scored Nim

答案是 ⌈ s u m 2 ⌉ \lceil \frac{sum}{2}\rceil 2sum

#include <bits/stdc++.h>

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  int ans = 0;
  while (n--) {
    int x;
    cin >> x;
    ans += x;
  }
  cout << (ans + 1) / 2 << "\n";
  return 0;
}

Milliarium Aureum

考虑从大到小加边,维护所有边和主要边的并查集,如果两个点在所有边的并查集中连通,在树边中不连通,则它们均不合法。启发式合并所有合法点即可。

#include <bits/stdc++.h>

using namespace std;

class dsu {
 public:
  vector<vector<int>> good;
  vector<int> sz;
  vector<int> p;
  int n;

  dsu(int n, bool add): n(n) {
    p.resize(n);
    sz.resize(n);
    good.resize(n);
    for (int i = 0; i < n; ++i) {
      p[i] = i;
      sz[i] = 1;
      if (add) {
        good[i].push_back(i);
      }
    }
  }

  int find(int x) {
    while (x != p[x]) {
      x = p[x] = p[p[x]];
    }
    return x;
  }

  bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x != y) {
      if (good[x].size() > good[y].size()) {
        swap(x, y);
      }
      p[x] = y;
      sz[y] += sz[x];
      for (auto z : good[x]) {
        good[y].push_back(z);
      }
      return true;
    } else {
      return false;
    }
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<int> type(m), from(m), to(m), cost(m), order(m);
  for (int i = 0; i < m; ++i) {
    cin >> type[i] >> from[i] >> to[i] >> cost[i];
    --from[i];
    --to[i];
    order[i] = i;
  }
  sort(order.begin(), order.end(), [&](int x, int y) {
    return cost[x] > cost[y];
  });
  dsu all(n, true), major(n, false);
  for (int i = 0, j = 0; i < m; i = j) {
    while (j < m && cost[order[j]] == cost[order[i]]) {
      ++j;
    }
    for (int k = i; k < j; ++k) {
      all.unite(from[order[k]], to[order[k]]);
      if (type[order[k]]) {
        major.unite(from[order[k]], to[order[k]]);
      }
    }
    for (int k = i; k < j; ++k) {
      if (all.sz[all.find(from[order[k]])] != major.sz[major.find(from[order[k]])]) {
        all.good[all.find(from[order[k]])].clear();
      }
    }
  }
  vector<int> ans = all.good[all.find(0)];
  sort(ans.begin(), ans.end());
  cout << ans.size() << "\n";
  for (int i = 0; i < (int) ans.size(); ++i) {
    if (i) {
      cout << " ";
    }
    cout << ans[i] + 1;
  }
  cout << "\n";
  return 0;
}

Permutant

p p p 的环多于两个时,答案为 0 0 0 。两个环例如环长是 a , b a, b a,b ,则用前 a a a 行减去后 a a a 行得到 0 0 0 向量,即矩阵不满秩;更多的环归纳即可。

剩下我们需要求 cyclic matrix 的行列式,可以证明其行列式为 ∏ i = 0 n A ( ω n i ) \prod_{i=0}^n A(\omega_n^i) i=0nA(ωni) ,注意到 ω n i \omega_n^i ωni B ( x ) = x n − 1 B(x)=x^n-1 B(x)=xn1 的所有根,则我们要求的就是 A A A B B Bresultant ,具体细节可以看官方题解或者wiki。

#include <bits/stdc++.h>

using namespace std;

const int md = (int) 1e9 + 7;

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (int) ((long long) x * y % md);
}

inline int inv(int a) {
  int b = md, u = 0, v = 1;
  while (a) {
    int t = b / a;
    b -= t * a;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  if (u < 0) {
    u += md;
  }
  return u;
}

int resultant(vector<int> a, vector<int> b) {
  int ans = 1;
  while (true) {
    int n = a.size() - 1;
    int m = b.size() - 1;
    if (n > m) {
      swap(n, m);
      swap(a, b);
      if ((n & 1) && (m & 1)) {
        ans = (md - ans) % md;
      }
    }
    if (!n) {
      for (int i = 0; i < m; ++i) {
        ans = mul(ans, a[0]);
      }
      return ans;
    }
    int coef = mul(b.back(), inv(a.back()));
    for (int i = 0; i < (int) a.size(); ++i) {
      sub(b[b.size() - a.size() + i], mul(a[i], coef));
    }
    while ((int) b.size() > 1 && b.back() == 0) {
      ans = mul(ans, a[n]);
      b.pop_back();
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    cin >> a[i];
  }
  vector<int> p(n);
  for (int i = 0; i < n; ++i) {
    cin >> p[i];
    --p[i];
  }
  vector<int> new_a;
  for (int i = 0; new_a.empty() || i; i = p[i]) {
    new_a.push_back(a[i]);
  }
  swap(a, new_a);
  if ((int) a.size() != n) {
    cout << 0 << "\n";
    return 0;
  }
  vector<int> c(n);
  for (int i = 0, j = 0; i < n; ++i, j = p[j]) {
    c[j] = (n - i) % n;
  }
  int ans = 1;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < i; ++j) {
      if (c[j] > c[i]) {
        ans = md - ans;
      }
    }
  }
  vector<int> b(n + 1);
  b[n] = 1;
  b[0] = md - 1;
  cout << mul(ans, resultant(a, b)) << "\n";
  return 0;
}

Game Of Chance

二分出平衡点的整数部分,小数部分可以直接算。

#include <bits/stdc++.h>

using namespace std;

const int md = (int) 1e9 + 7;

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (int) ((long long) x * y % md);
}

inline int inv(int a) {
  int b = md, u = 0, v = 1;
  while (a) {
    int t = b / a;
    b -= t * a;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  if (u < 0) {
    u += md;
  }
  return u;
}

long long sum(int n) {
  return (long long) n * (n + 1) / 2;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    int m;
    cin >> m;
    int l = 1, r = m;
    while (l < r) {
      int mid = (l + r + 1) >> 1;
      if (sum(mid - 1) + sum(m - mid) >= (long long) mid * m) {
        l = mid;
      } else {
        r = mid - 1;
      }
    }
    int ans = (m + l + 1) % md;
    sub(ans, mul(mul(l, l + 1), inv(m - l)));
    ans = mul(ans, (md + 1) / 4);
    cout << ans << "\n";
  }
  return 0;
}

Incomparable Pairs

将问题转化成有子串关系的对数。注意到后缀自动机一个节点对应的串可以表示成 [ l , r ] [l, r] [l,r] ,其中 $ l\in [a, b], r=c$ 。我们需要这些区间的本质不同子串个数。区间本质不同子串个数是经典问题,注意到其做法的线段树可以维护这个问题的左端点在一个区间,右端点固定的答案,直接维护就好了。

#include <bits/stdc++.h>

using namespace std;

const int N = 223456;

int n, cnt, total, ed[N], pr[N], len[N], pos[N], nxt[N][26];
vector<pair<int, int>> events[N];
pair<int, int> modifies[N];
unsigned long long res[N];
vector<int> adj[N];
char s[N];

int extend(int p, int w) {
  int np = ++total;
  len[np] = len[p] + 1;
  while (p && !nxt[p][w]) {
    nxt[p][w] = np;
    p = pr[p];
  }
  if (!p) {
    pr[np] = 1;
  } else {
    int q = nxt[p][w];
    if (len[q] == len[p] + 1) {
      pr[np] = q;
    } else {
      int nq = ++total;
      len[nq] = len[p] + 1;
      memcpy(nxt[nq], nxt[q], sizeof nxt[nq]);
      pr[nq] = pr[q];
      pr[q] = pr[np] = nq;
      while (p && nxt[p][w] == q) {
        nxt[p][w] = nq;
        p = pr[p];
      }
    }
  }
  return np;
}

namespace lct {
int f[N], tag[N], last[N], c[N][2];

void mark(int x, int v) {
  tag[x] = last[x] = v;
}

void push(int x) {
  if (tag[x]) {
    if (c[x][0]) {
      mark(c[x][0], tag[x]);
    }
    if (c[x][1]) {
      mark(c[x][1], tag[x]);
    }
    tag[x] = 0;
  }
}

bool is_root(int x) {
  return c[f[x]][0] != x && c[f[x]][1] != x;
}

void rotate(int x) {
  int y = f[x], z = f[y], k = c[y][1] == x;
  if (!is_root(y)) {
    c[z][c[z][1] == y] = x;
  }
  f[c[y][k] = c[x][!k]] = y;
  f[f[c[x][!k] = y] = x] = z;
}

void splay(int x) {
  static int st[N];
  int top = 0;
  st[++top] = x;
  for (int i = x; !is_root(i); i = f[i]) {
    st[++top] = f[i];
  }
  while (top) {
    push(st[top--]);
  }
  while (!is_root(x)) {
    int y = f[x], z = f[y];
    if (!is_root(y)) {
      rotate((c[y][1] == x) == (c[z][1] == y) ? y : x);
    }
    rotate(x);
  }
}

void access(int x, int v) {
  int t = 0;
  cnt = 0;
  while (x) {
    splay(x);
    modifies[++cnt] = make_pair(len[x], last[x]);
    c[x][1] = t;
    mark(x, v);
    t = x;
    x = f[x];
  }
}
}

namespace segtree {
unsigned long long add[N], sum[N], sum2[N];

void apply(int x, int l, int r, unsigned long long v) {
  add[x] += v;
  sum[x] += v * (r - l + 1);
  sum2[x] += v * ((unsigned long long) (r - l + 1) * (l + r) / 2);
}

void pull(int x, int z) {
  sum[x] = sum[x + 1] + sum[z];
  sum2[x] = sum2[x + 1] + sum2[z];
}

void push(int x, int l, int r) {
  int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
  if (add[x]) {
    apply(x + 1, l, y, add[x]);
    apply(z, y + 1, r, add[x]);
    add[x] = 0;
  }
}

void modify(int x, int l, int r, int ll, int rr, int v) {
  if (ll <= l && r <= rr) {
    apply(x, l, r, v);
  } else {
    int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
    push(x, l, r);
    if (ll <= y) {
      modify(x + 1, l, y, ll, rr, v);
    }
    if (rr > y) {
      modify(z, y + 1, r, ll, rr, v);
    }
    pull(x, z);
  }
}

unsigned long long query_sum(int x, int l, int r, int ll, int rr) {
  if (ll <= l && r <= rr) {
    return sum[x];
  } else {
    int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
    unsigned long long ans = 0;
    push(x, l, r);
    if (ll <= y) {
      ans += query_sum(x + 1, l, y, ll, rr);
    }
    if (rr > y) {
      ans += query_sum(z, y + 1, r, ll, rr);
    }
    return ans;
  }
}

unsigned long long query_sum2(int x, int l, int r, int ll, int rr) {
  if (ll <= l && r <= rr) {
    return sum2[x];
  } else {
    int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
    unsigned long long ans = 0;
    push(x, l, r);
    if (ll <= y) {
      ans += query_sum2(x + 1, l, y, ll, rr);
    }
    if (rr > y) {
      ans += query_sum2(z, y + 1, r, ll, rr);
    }
    return ans;
  }
}
}

void dfs(int x) {
  for (auto y : adj[x]) {
    dfs(y);
    ed[x] = ed[y];
  }
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  scanf("%s", s + 1);
  n = strlen(s + 1);
  pos[0] = ++total;
  for (int i = 1; i <= n; ++i) {
    pos[i] = extend(pos[i - 1], s[i] - 'a');
    ed[pos[i]] = i;
  }
  for (int i = 2; i <= total; ++i) {
    adj[pr[i]].push_back(i);
  }
  dfs(1);
  for (int i = 2; i <= total; ++i) {
    events[ed[i]].emplace_back(ed[i] - len[i] + 1, ed[i] - len[pr[i]]);
  }
  unsigned long long ans = 0;
  for (int i = 2; i <= total; ++i) {
    ans += len[i] - len[pr[i]];
  }
  if (ans % 2 == 0) {
    ans = ans / 2 * (ans + 1);
  } else {
    ans = (ans + 1) / 2 * ans;
  }
  for (int i = 1; i <= total; ++i) {
    lct::f[i] = pr[i];
  }
  for (int i = 1; i <= n; ++i) {
    segtree::modify(1, 1, n, 1, i, 1);
    lct::access(pos[i], i);
    int last = 0;
    for (int j = cnt; j > 1; --j) {
      pair<int, int> p = modifies[j];
      if (p.first) {
        if (p.second) {
          segtree::modify(1, 1, n, p.second - p.first + 1, p.second - last, -1);
        }
        last = p.first;
      }
    }
    for (auto p : events[i]) {
      int l = p.first, r = p.second;
      ans -= segtree::query_sum2(1, 1, n, l, r);
      ans += segtree::query_sum(1, 1, n, l, r) * (l - 1);
      if (r != i) {
        ans -= segtree::query_sum(1, 1, n, r + 1, i) * (r - l + 1);
      }
    }
  }
  printf("%llu\n", ans);
  return 0;
}

The Zong of the Zee

当没有 ? 的时候,即为前 m − 1 m-1 m1 行和后 m − 1 m-1 m1 行匹配的方案数。有 ? 可以枚举 ? 是啥,然后减掉多算的。

#include <bits/stdc++.h>

using namespace std;

const int MAX = 128;
const int md = (int) 1e9 + 7;
const int base = 2333;
const int md0 = (int) 1e9 + 7;
const int md1 = (int) 1e9 + 9;

struct hashv {
  int h0, h1;

  hashv(int h0 = 0, int h1 = 0): h0(h0), h1(h1) {
  }

  hashv operator * (const int &x) const {
    return hashv((long long) h0 * x % md0, (long long) h1 * x % md1);
  }

  hashv operator + (const hashv &x) const {
    return hashv((h0 + x.h0) % md0, (h1 + x.h1) % md1);
  };

  hashv operator - (const hashv &x) const {
    return hashv((h0 + md0 - x.h0) % md0, (h1 + md1 - x.h1) % md1);
  };

  hashv operator * (const hashv &x) const {
    return hashv((long long) h0 * x.h0 % md0, (long long) h1 * x.h1 % md1);
  }

  inline long long get() {
    return (long long) h0 * md1 + h1;
  }
};

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (int) ((long long) x * y % md);
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m;
  cin >> m >> n;
  vector<string> board(m);
  vector<int> least(MAX);
  vector<int> pos(m, -1);
  for (int i = 0; i < m; ++i) {
    cin >> board[i];
    vector<int> cnt(MAX);
    for (int j = 0; j < n; ++j) {
      if (board[i][j] == '?') {
        pos[i] = j;
      } else {
        ++cnt[board[i][j]];
      }
    }
    for (int j = 0; j < MAX; ++j) {
      least[j] = max(least[j], cnt[j]);
    }
  }
  int sum_least = 0;
  for (int i = 0; i < MAX; ++i) {
    sum_least += least[i];
  }
  if (sum_least > n) {
    cout << -1 << "\n";
    return 0;
  }
  for (int i = 0; i < m; ++i) {
    vector<int> cnt(MAX);
    for (int j = 0; j < n; ++j) {
      if (board[i][j] != '?') {
        ++cnt[board[i][j]];
      }
    }
    for (int j = 0; j < MAX; ++j) {
      if (cnt[j] != least[j]) {
        board[i][pos[i]] = j;
        pos[i] = -1;
      }
    }
  }
  vector<hashv> power(m);
  power[0] = hashv(1, 1);
  for (int i = 1; i < m; ++i) {
    power[i] = power[i - 1] * base;
  }
  vector<hashv> up(n), down(n);
  for (int i = 0; i + 1 < m; ++i) {
    for (int j = 0; j < n; ++j) {
      if (board[i][j] != '?') {
        up[j] = up[j] + power[i] * board[i][j];
      }
      if (board[i + 1][j] != '?') {
        down[j] = down[j] + power[i] * board[i + 1][j];
      }
    }
  }
  auto solve = [&](int question) {
    vector<hashv> new_up = up, new_down = down;
    for (int i = 0; i + 1 < m; ++i) {
      if (pos[i] != -1) {
        new_up[pos[i]] = new_up[pos[i]] + power[i] * question;
      }
      if (pos[i + 1] != -1) {
        new_down[pos[i + 1]] = new_down[pos[i + 1]] + power[i] * question;
      }
    }
    unordered_map<long long, int> s;
    int ans = 1;
    for (int i = 0; i < n; ++i) {
      ++s[new_up[i].get()];
    }
    for (int i = 0; i < n; ++i) {
      ans = mul(ans, s[new_down[i].get()]--);
    }
    return ans;
  };
  int ans = 0;
  for (int i = 33; i <= 126; ++i) {
    if (i != 63) {
      add(ans, solve(i));
    }
  }
  sub(ans, mul(solve(63), 92));
  cout << ans << "\n";
  return 0;
}

Expected Value

答案是 a 1 − a 2 a_1 - a_2 a1a2

#include <bits/stdc++.h>

using namespace std;

const int md = (int) 1e9 + 7;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int foo, bar, baz;
  cin >> foo >> bar >> baz;
  cout << (bar + md - baz) % md << "\n";
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值