多校训练营4

C:Easy Counting Problem

z3475

标签

数学;FFT

题意

个数字,第个数字出现至少次的组合数量。

思路

容易得到答案为

将右式看作一个整体,左式即为,而的级数第j项即位求出。暴力计算表层的累乘,右式的项相乘使用NTT计算,左式特殊处理即可。

代码
参考代码
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#include <bits/stdc++.h>
using namespace std;
struct FAST_IO {
  FAST_IO() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
  }
} __fast_io;
#define all(a) (a).begin(), (a).end()
auto& _ = std::ignore;
using ll = long long;
template <class T>
using vec = vector<T>;

bool MULTIDATA = true;
#ifndef __OP__
#define __OP__
#define def_op(op)                                  \
  this_type operator op(const this_type& a) const { \
    this_type k(*this);                             \
    k op## = a;                                     \
    return k;                                       \
  }
#define def_cmp(op, n2)                        \
  bool operator op(const this_type& a) const { \
    return n2 op a.n2;                         \
  }
#define def_all_cmp(n2)                                         \
  def_cmp(<, n2) def_cmp(>, n2) def_cmp(<=, n2) def_cmp(>=, n2) \
      def_cmp(!=, n2) def_cmp(==, n2)
#endif
#define ATL_MATH
constexpr ll gcd(ll a, ll b) {
  return b ? gcd(b, a % b) : a;
}
constexpr ll lcm(ll a, ll b) {
  return a * b / gcd(a, b);
}
template <class T>
T power(T a, size_t b, const T& unit = 1) {
  if (b == 0)
    return unit;
  if (b & 1)
    return a * power(a * a, b >> 1, unit);
  return power(a * a, b >> 1, unit);
}
constexpr ll ceildiv(const ll a, const ll b) {
  return a / b + (a % b ? 1 : 0);
}
tuple<ll, ll, ll> exgcd(ll a, ll b) {  // a1+b2=gcd(a,b)
  if (b == 0)
    return make_tuple(a, 1, 0);
  ll g, x, y;
  tie(g, x, y) = exgcd(b, a % b);
  return make_tuple(g, y, x - a / b * y);
}
tuple<ll, ll, ll> Fexgcd(ll a, ll b) {  // a1+b2=gcd(a,b),ensure 1>0
  auto k = exgcd(a, b);
  if (get<1>(k) < 0) {
    get<1>(k) += b;
    get<2>(k) -= a;
  }
  return k;
}
template <class T, class uT, ll mod>
struct _mint {
  using this_type = _mint;
  T v;
  _mint() = default;
  template <class iT>
  constexpr _mint(const iT& a) {
    v = a % mod;
    v += v < 0 ? mod : 0;
  }
  template <class iT>
  static _mint from(const iT& v) {
    _mint a;
    a.v = v;
    return a;
  }
  _mint& operator+=(const _mint& a) {
    return (v += a.v) >= mod && (v -= mod), *this;
  }
  _mint& operator-=(const _mint& a) {
    return (v -= a.v) < 0 && (v += mod), *this;
  }
  _mint& operator*=(const _mint& a) { return (v = ((uT)v * a.v) % mod), *this; }
  def_op(+) def_op(-) def_op(*) def_all_cmp(v)
#ifdef ATL_MATH
      _mint inverse() const {
    auto c = exgcd(v, mod);
    _mint s;
    s.v = get<1>(c) < 0 ? get<1>(c) + mod : get<1>(c);
    return s;
  }
  _mint& operator/=(const _mint& a) {
    return (*this) *= a.inverse(), *this;
  }
  def_op(/)
#endif
};
template <class T, class uT, ll mod>
ostream& operator<<(ostream& os, const _mint<T, uT, mod>& a) {
  return os << a.v;
}
template <class T, class uT, ll mod>
istream& operator>>(istream& os, _mint<T, uT, mod>& a) {
  T k;
  os >> k;
  a = _mint<T, uT, mod>(k);
  return os;
}
using mint = _mint<int, long long, 998244353>;
using mll = _mint<long long, __int128, 998244353>;

template <class T>
struct ntt {
  constexpr static int l = 16, n = 1 << 16;
  array<int, n> r{};
  const int mod{998244353};
  const array<const T, 2> P{3, 332748118};
  array<array<T, n>, 2> W{};
  int floorintlog2(int i) {
    int k = 0;
    while (i)
      i >>= 1, k++;
    cout << k << endl;
    return k;
  }
  ntt() {
    for (int i = 0; i < n; i++)
      r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    for (int type : {0, 1})
      for (int i = 0; i < l; i++)
        W[type][i] = power(P[type], (mod - 1) / (1 << i << 1));
  }
  template <int type, class U>
  valarray<T> _NTT(const U& B) const {
    valarray<T> A(n);
    copy(std::begin(B), std::end(B), begin(A));
    for (int i = 0; i < n; i++)
      if (i < r[i])
        swap(A[i], A[r[i]]);
    for (int mid = 1, midj = 0; mid < n; mid <<= 1, midj++) {
      T Wn = W[type][midj];
      for (int R = mid << 1, j = 0; j < n; j += R) {
        T w = T::from(1);
        for (int k = 0; k < mid; k++, w *= Wn) {
          const T x = A[j + k], y = w * A[j + mid + k];
          A[j + k] = x + y;
          A[j + mid + k] = x - y;
        }
      }
    }
    if (type)
      A *= power(T(n), mod - 2);
    return A;
  }
  template <class U>
  valarray<T> NTT(const U& A) const {
    return _NTT<0>(A);
  }
  valarray<T> DNT(const valarray<T>& A) const { return _NTT<1>(A); }
};
template <class T>
struct RANGE_ {
  T b, e;
  T begin() const& { return b; };
  T end() const& { return e; };
};
template <class T>
RANGE_<T> RANGE(T a, T b) {
  return RANGE_<T>{a, b};
}

#define N (50000 + 10)
mint P[10000000 + 10] = {1}, NIP[N] = {-1}, IP[10000000 + 10];
int w;
int c[100];
ntt<mint> G;
array<valarray<mint>, 11> cv, f;
int C, LC;
#define NN (10000000 / 2 + 1)
mint expa(int a, int i) {
  if (a == 0)
    return i == 0 ? 1 : 0;
  return IP[i];
}

int main() {
  for (mint i = 1; i.v <= 10000000; i.v++) {
    P[i.v] = P[i.v - 1] * i;
  }
  for (int i = 1; i < N; i++)
    NIP[i] = NIP[0] / P[i];
  for (int i = 0; i <= 10000000; i++) {
    IP[i] = P[i].inverse();
  }
  cin >> w;
  for (int i = 0; i < w; i++)
    cin >> c[i];
  f.fill(valarray<mint>(0, N));
  f[0][0] = 1;
  for (int i = 0; i < w; i++) {
    cv[i] = G.NTT(RANGE(&NIP[0], &NIP[c[i]]));
  }
  for (int i = 0; i < w; i++) {
    for (int j = w; j >= 1; j--) {
      f[j] = f[j - 1] + G.DNT(G.NTT(f[j]) * cv[i]);
    }
    f[0] = G.DNT(G.NTT(f[0]) * cv[i]);
  }
  int q;
  cin >> q;
  while (q--) {
    int n;
    cin >> n;
    mint ans = 0;
    int nn = min(n, 50000);
    for (int j = 0; j <= w; j++) {
      mint k = power(mint(j), n - nn);
      for (int i = nn; i >= 0; i--) {
        ans += f[j][i] * k * IP[n - i];
        k *= j;
      }
    }
    cout << ans * P[n] << endl;
  }
}

E:Jobs (Hard Version)

z3475

标签

前缀和;单调队列

题意

有n个公司,每个公司有m个职位要求,如果个人能力值三元组都大于职位要求,公司就会给他发offer。q次问一个人收到多少offer。

思路

显然要求回答询问,离线看起来不好搞,如果能够回答询问即必须求出每个(i,j,k)询问对应的公司数。

不考虑复杂度的情况下,每个公司有一个存二进制的三维数组,最终三维数组求和即可(easy ver)。

(hard ver)考虑二维问题,发现职位要求能代替。这是一个单调队列like的问题,经过排除后的操作后,则两维是严格负相关的。对于排序后相邻的(a,b)和(c,d),其对每个公司的二维二进制数组的影响如图

imges

差分后即绿点++,红点--。开一个全局二维数组,在上面修改即可避免空间过大。整完求个二维前缀和即可。

步入三维,首先不能在全局二维数组上粗暴扩展维度,因为公司数量的合并是非常麻烦的。考虑仍然求每个公司的三维二进制数组,每个公司现在有m个这样的二维问题,发现高度(z轴)为a的平面得到的形状最终要合并所有高度(z轴)为b的平面的形状,。考虑按z轴从小到大维护这个形状的数据结构,即可维护成功。同样开全局三维数组,最终求个三维前缀和就行。

代码
参考代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <bits/stdc++.h>
using namespace std;
struct FAST_IO {
  FAST_IO() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
  }
} __fast_io;
#define x first
#define y second
#define all(a) (a).begin(), (a).end()
using ll = long long;
template <class T>
using vec = vector<T>;

bool MULTIDATA = true;

#define mod (998244353)
int seed, n, q, m;
int D[405][405][405], A[405][405][405];

void insert(map<int, int>& m, const int x, const int y, const int z) {
  auto maintain = [&](map<int, int>::iterator i, const int a) {  // insert=1
    auto ri = reverse_iterator<map<int, int>::iterator>(i);
    auto ii = next(i);
    D[i->x][i->y][z] += a;
    if (ii != m.end())
      D[ii->x][i->y][z] -= a;
    if (ri != m.rend())
      D[i->x][ri->y][z] -= a;
    if (ii != m.end() && ri != m.rend())
      D[ii->x][ri->y][z] += a;
  };
  auto w = m.upper_bound(x);
  if (w != m.begin()) {
    auto pw = prev(w);
    if (pw->y <= y)
      return;
    if (pw->x == x) {
      maintain(pw, -1);
      m.erase(pw);
    }
  }
  for (auto wn = m.upper_bound(x); wn != m.end() && wn->y >= y;) {
    maintain(wn, -1);
    wn = m.erase(wn);
  }
  auto mi = m.insert({x, y});
  w = mi.x;
  maintain(w, 1);
}
int solve(int a, int b, int c) {
  return D[a][b][c];
}
int main() {
  cin >> n >> q;
  for (int t = 0; t < n; t++) {
    int m;
    cin >> m;
    vec<array<int, 3>> mv;
    for (int j = 1; j <= m; j++) {
      int a, b, c;
      cin >> a >> b >> c;
      mv.push_back({c, a, b});
    }
    sort(all(mv));
    map<int, int> F;
    for (const auto& [z, x, y] : mv) {
      insert(F, x, y, z);
    }
  }

  for (int i = 1; i <= 400; i++)
    for (int j = 1; j <= 400; j++)
      for (int k = 1; k <= 400; k++)
        D[i][j][k] += D[i - 1][j][k] + D[i][j - 1][k] + D[i][j][k - 1] -
                      D[i - 1][j - 1][k] - D[i - 1][j][k - 1] -
                      D[i][j - 1][k - 1] + D[i - 1][j - 1][k - 1];
  cin >> seed;
  mt19937 rng(seed);
  uniform_int_distribution<> u(1, 400);
  int lastans = 0;
  ll ans = 0;
  for (int i = 1; i <= q; i++) {
    int IQ = (u(rng) ^ lastans) % 400 + 1;  // The IQ of the i-th friend
    int EQ = (u(rng) ^ lastans) % 400 + 1;  // The EQ of the i-th friend
    int AQ = (u(rng) ^ lastans) % 400 + 1;  // The AQ of the i-th friend
    lastans = solve(IQ, EQ, AQ);            // The answer to the i-th friend
    ans = (ans * seed + lastans) % mod;
  }
  cout << ans << endl;
}

L:Black Hole


评论