2018 集训队互测 Day 1 简要题解

完美的队列:

如果能求出每个加入的数在所有队列中都被移出去的时刻,那就可以很方便地计算答案了。

对序列分块,先考虑整块的,更新跨过整个块的区间答案,记 bi b i 表示队列要弹出当前的数需要的容量,那么每次操作实际上是对 bi b i 进行区间减法,注意到大部分时候是对整块打标记,如果不是整块打标记直接暴力就行了。当 所有 bi<0 b i < 0 的时候,就是所有东西都被弹出去的时候,可以利用双指针更新答案。

离散的块实际上也可以类似处理,预处理跨过整块的东西的前缀和,同样枚举每个位置和跨过它的区间,然后类似整块也可以双指针求出答案,这部分细节详见代码。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 100005;
const int B = 325;
const int inf = 0x3f3f3f3f;

int n, m, ans, a[N], b[N], f[N], l[N], r[N], x[N], all[N], sum[N], pos[N], qry[N];
vector <int> v[N];

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), Read(m);
  for (int i = 1; i <= n; ++i) {
    Read(a[i]);
  }
  for (int i = 1; i <= m; ++i) {
    Read(l[i]), Read(r[i]), Read(x[i]);
  }
  for (int L = 1, R; L <= n; L += B) {
    R = min(L + B - 1, n);
    int val = -inf, tag = 0, cnt = 0, tot = 0;
    for (int i = L; i <= R; ++i) {
      b[i] = a[i], CheckMax(val, b[i]);
    }
    for (int i = 1, j = 2; i <= m; ++i) {
      if (i > 1 && val + tag <= 0) {
        if (l[i] <= L && R <= r[i]) {
          ++tag;
        } else if (l[i] <= R && L <= r[i]) {
          val = -inf;
          for (int k = L; k <= R; ++k) {
            if (k >= l[i] && k <= r[i]) {
              ++b[k];
            }
            CheckMax(val, b[k]);
          }
        }
      }
      for (; val + tag > 0 && j <= m; ++j) {
        if (l[j] <= L && R <= r[j]) {
          --tag;
        } else if (l[j] <= R && L <= r[j]) {
          val = -inf;
          for (int k = L; k <= R; ++k) {
            if (k >= l[j] && k <= r[j]) {
              --b[k];
            }
            CheckMax(val, b[k]);
          }
        }
      }
      sum[i] = sum[i - 1];
      if (l[i] <= L && R <= r[i]) {
        CheckMax(f[i], val + tag > 0 ? m + 1 : j - 1);
        ++sum[i], all[++tot] = i;
      } else if (l[i] <= R && L <= r[i]) {
        qry[++cnt] = i, pos[cnt] = tot;
      }
    }
    for (int i = L; i <= R; ++i) {
      for (int j = 1, k = 2, val = a[i]; j <= cnt; ++j) {
        if (j > 1) {
          val += sum[qry[j]] - sum[qry[j - 1]];
          if (l[qry[j]] <= i && i <= r[qry[j]]) {
            ++val;
          }
        }
        for (; val > 0 && k <= cnt; ++k) {
          val -= sum[qry[k]] - sum[qry[k - 1]];
          if (l[qry[k]] <= i && i <= r[qry[k]]) {
            --val;
          }
        }
        if (l[qry[j]] <= i && i <= r[qry[j]]) {
          if (val > 0) {
            CheckMax(f[qry[j]], sum[m] - sum[qry[cnt]] < val ? m + 1 : all[pos[cnt] + val]);
          } else {
            CheckMax(f[qry[j]], l[qry[k - 1]] <= i && i <= r[qry[k - 1]] ? val ? all[pos[k - 1] + val + 1] : qry[k - 1] : all[pos[k - 1] + val]);
          }
        }
      }
    }
  }
  memset(sum, 0, sizeof sum);
  for (int i = 1; i <= m; ++i) {
    if (!sum[x[i]]++) {
      ++ans;
    }
    v[f[i]].pb(x[i]);
    for (auto x : v[i]) {
      if (!--sum[x]) {
        --ans;
      }
    }
    printf("%d\n", ans);
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

完美的集合:

注意到合法的关键点连成了一棵树,所以可以容斥,用 i i 合法的减去 i pari p a r i 都合法的。

枚举之后将树处理出来,变成了一棵有根树,可以利用DFS序DP求出最大权值以及方案数。

剩下就是组合数取模的问题了,就是要快速求 1in,imod50i ∏ 1 ≤ i ≤ n , i mod 5 ≠ 0 i ,考虑维护一个多项式 fn(x)=1in,imod50(x+i) f n ( x ) = ∏ 1 ≤ i ≤ n , i mod 5 ≠ 0 ( x + i ) ,然后找到一个最大的 k k 满足 10kn ,那么考虑计算 f10k(x) f 10 k ( x ) ,剩下的暴力乘上去。 f10k(x)=f5k(x)+f5k(x+5k) f 10 k ( x ) = f 5 k ( x ) + f 5 k ( x + 5 k ) ,而注意到每次展开 f(x+5k) f ( x + 5 k ) 的时候,到了 23 23 项之后它的贡献就变成 0 0 了,所以多项式次数不超过 23 ,暴力维护就行了。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 65;
const int M = 10005;
const LL mod = 11920928955078125;

LL ans, lim, val_max, pwd[N], c[N][N], f[N][M], g[N][M];
int n, m, k, tim, v[N], w[N], dfn[N], seq[N], siz[N];
vector <pii> adj[N];
bool b[N];

inline LL Qul(LL x, LL y) {
  return (__int128)x * y % mod;
}

inline LL Qow(LL x, LL y) {
  LL r = 1;
  for (; y; y >>= 1, x = Qul(x, x)) {
    if (y & 1) {
      r = Qul(r, x);
    }
  }
  return r;
}

struct Poly {
  LL a[N];

  Poly() {
    for (int i = 0; i < 23; ++i) {
      a[i] = 0;
    }
  }

  Poly operator * (const Poly &b) const {
    Poly c;
    for (int i = 0; i < 23; ++i) {
      for (int j = 0; j <= i; ++j) {
        c.a[i] = (c.a[i] + Qul(a[j], b.a[i - j])) % mod;
      }
    }
    return c;
  }

  inline Poly Extend(LL x) {
    Poly b;
    pwd[0] = 1;
    for (int i = 1; i < 23; ++i) {
      pwd[i] = Qul(pwd[i - 1], x);
    }
    for (int i = 0; i < 23; ++i) {
      for (int j = i; j < 23; ++j) {
        b.a[i] = (Qul(a[j], Qul(pwd[j - i], c[j][i])) + b.a[i]) % mod;
      }
    }
    return b;
  }
};

map <LL, Poly> h;

inline Poly G(LL n) {
  if (h.find(n) != h.end()) {
    return h[n];
  }
  Poly r, t;
  r.a[0] = 1;
  if (n <= 20) {
    for (int i = 1; i <= n; ++i) {
      if (i % 5) {
        t.a[0] = i, t.a[1] = 1, r = r * t;
      }
    }
  } else {
    LL val = n / 2 / 5 * 5;
    Poly u = G(val - 1), v = u.Extend(val);
    r = u * v;
    for (LL i = val << 1; i <= n; ++i) {
      if (i % 5) {
        t.a[0] = i, t.a[1] = 1, r = r * t;
      }
    }
  }
  return h[n] = r;
}

inline LL F(LL n) {
  LL r = 1;
  for (LL i = 1; i <= n; i *= 5) {
    r = Qul(r, G(n / i).a[0]);
  }
  return r;
}

inline LL C(LL x, LL y) {
  if (x < y) {
    return 0;
  }
  int c = 0;
  for (LL i = 5; i <= x; i *= 5) {
    c += x / i - y / i - (x - y) / i;
  }
  if (c >= 23) {
    return 0;
  }
  LL r = 1;
  for (int i = 1; i <= c; ++i) {
    r = 5 * r % mod;
  }
  return Qul(Qul(r, F(x)), Qow(Qul(F(y), F(x - y)), mod / 5 * 4 - 1));
}

inline void Build(int x, int p, LL d) {
  if (v[x] && d >= (lim + v[x] - 1) / v[x]) {
    b[x] = false;
    return ;
  }
  for (auto e : adj[x]) {
    if (e.X != p) {
      Build(e.X, x, d + e.Y);
    }
  }
}

inline void DFS(int x, int p) {
  seq[dfn[x] = ++tim] = x, siz[x] = 1;
  for (auto e : adj[x]) {
    int y = e.X;
    if (y != p && b[y]) {
      DFS(y, x), siz[x] += siz[y];
    }
  }
}

inline void DP() {
  for (int i = tim; i; --i) {
    int x = seq[i];
    for (int j = 0; j <= m; ++j) {
      if (j < w[x]) {
        f[x][j] = f[seq[i + siz[x]]][j], g[x][j] = g[seq[i + siz[x]]][j];
      } else {
        if (f[seq[i + siz[x]]][j] > f[seq[i + 1]][j - w[x]] + v[x]) {
          f[x][j] = f[seq[i + siz[x]]][j];
          g[x][j] = g[seq[i + siz[x]]][j];
        } else if (f[seq[i + siz[x]]][j] < f[seq[i + 1]][j - w[x]] + v[x]) {
          f[x][j] = f[seq[i + 1]][j - w[x]] + v[x];
          g[x][j] = g[seq[i + 1]][j - w[x]];
        } else {
          f[x][j] = f[seq[i + siz[x]]][j];
          g[x][j] = g[seq[i + siz[x]]][j] + g[seq[i + 1]][j - w[x]];
        }
      }
    }
  }
}

inline pair <LL, LL> Solve(int x) {
  tim = 0, DFS(x, 0), seq[tim + 1] = 0, DP(), --g[x][0];
  LL u = 0, v = 0;
  for (int i = 0; i <= m; ++i) {
    if (CheckMax(u, f[x][i])) {
      v = g[x][i];
    } else if (u == f[x][i]) {
      v += g[x][i];
    }
  }
  return mp(u, v);
}

inline pair <LL, LL> Solve(int x, int y) {
  tim = 0, DFS(x, y), seq[tim + 1] = 0, DP(), --g[x][0];
  tim = 0, DFS(y, x), seq[tim + 1] = 0, DP(), --g[y][0];
  for (int i = 1; i <= m; ++i) {
    if (CheckMax(f[x][i], f[x][i - 1])) {
      g[x][i] = g[x][i - 1];
    } else if (f[x][i] == f[x][i - 1]) {
      g[x][i] += g[x][i - 1];
    }
  }
  LL u = 0, v = 0;
  for (int i = 0; i <= m; ++i) {
    if (CheckMax(u, f[x][i] + f[y][m - i])) {
      v = g[x][i] * g[y][m - i];
    } else if (u == f[x][i] + f[y][m - i]) {
      v += g[x][i] * g[y][m - i];
    }
  }
  return mp(u, v);
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), Read(m), Read(k), Read(lim), ++lim, g[0][0] = 1;
  for (int i = 0; i <= 23; ++i) {
    c[i][0] = 1;
    for (int j = 1; j <= i; ++j) {
      c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
    }
  }
  for (int i = 1; i <= m; ++i) {
    f[0][i] = -(1LL << 60);
  }
  for (int i = 1; i <= n; ++i) {
    Read(w[i]);
  }
  for (int i = 1; i <= n; ++i) {
    Read(v[i]);
  }
  for (int i = 1, x, y, w; i < n; ++i) {
    Read(x), Read(y), Read(w);
    adj[x].pb(mp(y, w)), adj[y].pb(mp(x, w));
  }
  for (int i = 1; i <= n; ++i) {
    b[i] = true;
  }
  for (int i = 1; i <= n; ++i) {
    CheckMax(val_max, Solve(i).X);
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= n; ++j) {
      b[j] = true;
    }
    Build(i, 0, 0);
    pair <LL, LL> ret = Solve(i);
    if (ret.X == val_max) {
      ans = (ans + C(ret.Y, k)) % mod;
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (auto e : adj[i]) {
      if (e.X > i) {
        for (int j = 1; j <= n; ++j) {
          b[j] = true;
        }
        int j = e.X;
        Build(i, 0, 0), Build(j, 0, 0);
        if (!b[i] || !b[j]) {
          continue;
        }
        pair <LL, LL> ret = Solve(i, j);
        if (ret.X == val_max) {
          ans = (ans - C(ret.Y, k) + mod) % mod;
        }
      }
    }
  }
  printf("%lld\n", ans);
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

完美的旅行:

首先预处理 f(i,j) f ( i , j ) 表示标号的与为 i i ,路径长度为 j 的方案数,这个相当于求 A1,A2,A3,,Am A 1 , A 2 , A 3 , ⋯ , A m

考虑暴力求出特征多项式之后,因为只关心 f(i,j) f ( i , j ) 的答案,可以直接对 f(i,j) f ( i , j ) 而不是整个矩阵进行线性变换,这样就把复杂度降下来了。

计算答案的时候,首先可以FWT求出集合交卷积对应的多项式 F F ,那么就是要求 F+F2+F3=11F1 ,多项式求逆即可。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 65;
const int M = 65540;
const int G = 3;
const int mod = 998244353;

int n, m, D, L, W[N], fac[N], inv[N], val[N], f[M], g[M], R[M], a[N][N], ans[N][M];

struct Matrix {
  int a[N][N];

  Matrix() {
    memset(a, 0, sizeof a);
  }

  Matrix operator * (const Matrix &b) const {
    Matrix c;
    for (int k = 0; k < n; ++k) {
      for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
          c.a[i][j] = (1LL * a[i][k] * b.a[k][j] + c.a[i][j]) % mod;
        }
      }
    }
    return c;
  }
} cur, trs;

inline int Qow(int x, int y) {
  int r = 1;
  for (; y; y >>= 1, x = 1LL * x * x % mod) {
    if (y & 1) {
      r = 1LL * r * x % mod;
    }
  }
  return r;
}

inline int Det() {
  int ret = 1;
  for (int i = 0; i < n; ++i) {
    int p = i;
    for (; p < n && !a[p][i]; ++p);
    if (p == n) {
      return 0;
    }
    if (i != p) {
      for (int j = i; j < n; ++j) {
        swap(a[p][j], a[i][j]);
      }
      ret = mod - ret;
    }
    ret = 1LL * ret * a[i][i] % mod;
    for (int j = i + 1; j < n; ++j) {
      if (a[j][i]) {
        int cof = 1LL * a[j][i] * Qow(a[i][i], mod - 2) % mod;
        for (int k = i; k < n; ++k) {
          a[j][k] = (a[j][k] - 1LL * a[i][k] * cof % mod + mod) % mod;
        }
      }
    }
  }
  return ret;
}

inline void Ini(int l) {
  for (D = 1, L = 0; D < l; D <<= 1, ++L);
  for (int i = 1; i < D; ++i) {
    R[i] = (R[i >> 1] >> 1) | ((i & 1) << L - 1);
  }
  W[0] = Qow(G, mod - 1 >> L);
  for (int i = 1; i < L; ++i) {
    W[i] = 1LL * W[i - 1] * W[i - 1] % mod;
  }
}

inline void IniInv() {
  W[0] = Qow(W[0], mod - 2);
  for (int i = 1; i < L; ++i) {
    W[i] = 1LL * W[i - 1] * W[i - 1] % mod;
  }
}

inline void DFT(int *x) {
  for (int i = 0; i < D; ++i) {
    if (i < R[i]) {
      swap(x[i], x[R[i]]);
    }
  }
  for (int i = 1, l = L - 1; i < D; i <<= 1, --l) {
    for (int j = 0; j < D; j += i << 1) {
      for (int k = 0, w = 1, u, v; k < i; ++k, w = 1LL * w * W[l] % mod) {
        u = x[j + k], v = 1LL * x[j + k + i] * w % mod, x[j + k] = (u + v) % mod, x[j + k + i] = (u - v + mod) % mod;
      }
    }
  }
}

inline void Inv(int *a, int n, int *b) {
  static int x[M], y[M];
  if (n == 1) {
    b[0] = Qow(a[0], mod - 2);
  } else {
    Inv(a, n + 1 >> 1, b), Ini(n << 1);
    for (int i = 0; i < D; ++i) {
      x[i] = i < n ? a[i] : 0, y[i] = i < n + 1 >> 1 ? b[i] : 0;
    }
    DFT(x), DFT(y);
    for (int i = 0; i < D; ++i) {
      x[i] = (2 - 1LL * x[i] * y[i] % mod + mod) * y[i] % mod;
    }
    IniInv(), DFT(x);
    for (int i = 0, v = Qow(D, mod - 2); i < n; ++i) {
      b[i] = 1LL * x[i] * v % mod;
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), Read(m), fac[0] = fac[1] = inv[0] = inv[1] = 1;
  for (int i = 2; i <= n; ++i) {
    fac[i] = 1LL * fac[i - 1] * i % mod;
    inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;
  }
  for (int i = 2; i <= n; ++i) {
    inv[i] = 1LL * inv[i - 1] * inv[i] % mod;
  }
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      Read(trs.a[i][j]), cur.a[i][j] = i == j;
    }
  }
  for (int i = 1; i <= n + 1; ++i) {
    cur = cur * trs;
    for (int j = 0; j < n; ++j) {
      for (int k = 0; k < n; ++k) {
        ans[j & k][i] = (ans[j & k][i] + cur.a[j][k]) % mod;
      }
    }
  }
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < n; ++j) {
      trs.a[i][j] = (mod - trs.a[i][j]) % mod;
    }
  }
  for (int i = 0; i <= n; ++i) {
    for (int j = 0; j < n; ++j) {
      for (int k = 0; k < n; ++k) {
        if (j == k) {
          a[j][k] = (trs.a[j][k] + i) % mod;
        } else {
          a[j][k] = trs.a[j][k];
        }
      }
    }
    val[i] = Det();
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = n; j >= i; --j) {
      val[j] = (val[j] - val[j - 1] + mod) % mod;
    }
  }
  f[0] = val[0], g[0] = 1;
  for (int i = 1; i <= n; ++i) {
    for (int j = i; j; --j) {
      g[j] = (g[j - 1] - 1LL * g[j] * (i - 1) % mod + mod) % mod;
    }
    val[i] = 1LL * val[i] * inv[i] % mod, g[0] = 0;
    for (int j = 1; j <= i; ++j) {
      f[j] = (1LL * val[i] * g[j] + f[j]) % mod;
    }
  }
  for (int i = 0; i < n; ++i) {
    for (int j = n + 2; j <= m; ++j) {
      for (int k = 0; k < n; ++k) {
        ans[i][j] = (ans[i][j] - 1LL * f[k] * ans[i][j - n + k] % mod + mod) % mod;
      }
    }
  }
  for (int t = 1; t <= m; ++t) {
    for (int i = 1; i < n; i <<= 1) {
      for (int j = 0; j < n; j += i << 1) {
        for (int k = 0; k < i; ++k) {
          ans[j + k][t] = (ans[j + k][t] + ans[j + k + i][t]) % mod;
        }
      }
    }
  }
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j <= m; ++j) {
      if (!j) {
        f[j] = 1;
      } else {
        f[j] = (mod - ans[i][j]) % mod;
      }
    }
    Inv(f, m + 1, g);
    for (int j = 1; j <= m; ++j) {
      ans[i][j] = g[j];
    }
  }
  for (int t = 1; t <= m; ++t) {
    for (int i = 1; i < n; i <<= 1) {
      for (int j = 0; j < n; j += i << 1) {
        for (int k = 0; k < i; ++k) {
          ans[j + k][t] = (ans[j + k][t] - ans[j + k + i][t] + mod) % mod;
        }
      }
    }
  }
  int ret = 0;
  for (int i = 0; i < n; ++i) {
    for (int j = 1; j <= m; ++j) {
      ret ^= ans[i][j];
    }
  }
  printf("%d\n", ret);
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值