Good Bye 2022 简要题解
从这里开始
过气选手留下了只会套路的眼泪。sad......
Problem A Koxia and Whiteboards
相信大家都会.jpg
Code
#include <bits/stdc++.h> using namespace std; typedef bool boolean; template <typename T> boolean vmin(T& a, T b) { return (a > b) ? (a = b, true) : (false); } template <typename T> boolean vmax(T& a, T b) { return (a < b) ? (a = b, true) : (false); } template <typename T> T smax(T x) { return x; } template <typename T, typename ...K> T smax(T a, const K &...args) { return max(a, smax(args...)); } template <typename T> T smin(T x) { return x; } template <typename T, typename ...K> T smin(T a, const K &...args) { return min(a, smin(args...)); } // debugging lib #define VN(x) #x #define Vmsg(x) VN(x) << " = " << (x) #define printv(x) cerr << VN(x) << " = " << (x); //#define debug(...) fprintf(stderr, __VA_ARGS__); template <typename A, typename B> ostream& operator << (ostream& os, const pair<A, B>& z) { os << "(" << z.first << ", " << z.second << ')'; return os; } template <typename T> ostream& operator << (ostream& os, const vector<T>& a) { boolean isfirst = true; os << "{"; for (auto z : a) { if (!isfirst) { os << ", "; } os << z; isfirst = false; } os << '}'; return os; } #define ll long long int T; int n, m; void solve() { scanf("%d%d", &n, &m); ll sum = 0; priority_queue<int> Q; for (int i = 1, x; i <= n; i++) { scanf("%d", &x); sum += x; Q.push(-x); } for (int i = 1, x; i <= m; i++) { scanf("%d", &x); sum += Q.top() + x; Q.pop(); Q.push(-x); } printf("%lld\n", sum); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem B Koxia and Permutation
当 $k = 1$ 的时候答案是 $2n$
当 $k > 1$ 的时候答案下界是 $n + 1$,像 $n, 1, n - 1, 2, \cdots$ 这样构造就行了
Code
#include <bits/stdc++.h> using namespace std; int T, n; void solve() { scanf("%d%*d", &n); int l = 1, r = n; for (int i = 1; i <= n; i++) { printf("%d%c", (i & 1) ? r-- : l++, i == n ? '\n' : ' '); } } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem C Koxia and Number Theory
题目相当于要使得 $1 = (a_i + x, a_j + x) = (a_i + x, a_j - a_i)$
假设它不是 $1$,而是 $p$ 的倍数,那么相当于要求 $x$ 模 $p$ 不能是某个值。
显然当 $x$ 模某个 $p$ 所有值都不能取的时候,答案为 NO,剩下都是 YES(考虑钦定模每个素数的余数 CRT 一定有解)
然后对 100 以内的质数暴力就可以了。
Code
#include <bits/stdc++.h> using namespace std; #define ll long long const int N = 105; int T, n; bitset<150> vis; vector<int> P; void get_primes() { const int L = 140; for (int i = 2; i < L; i++) { if (!vis.test(i)) { P.push_back(i); } for (int j = i * i; j < L; j += i) { vis.set(j); } } } ll a[N]; bool flg[150]; void solve() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lld", a + i); } sort(a + 1, a + n + 1); for (int i = 2; i <= n; i++) { if (a[i] == a[i - 1]) { puts("NO"); return; } } for (auto p : P) { for (int r = 0; r <= p; r++) flg[r] = false; for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { ll d = a[j] - a[i]; if (d % p) { continue; } flg[a[i] % p] = true; } } int q = 0; while (flg[q]) q++; if (q >= p) { puts("NO"); return; } } puts("YES"); } int main() { get_primes(); scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem D Koxia and Game
容易发现 Koxia 只有一种可选项。否则考虑 Koxia 最后有选择机会的那次,剩下 Mahiru 删完都只剩 2 个一样的,如果 Koxia 能有 2 个选择,那她可以选择让它无法形成排列的那个。
因此每一列只有两种数,Mahiru 会删掉少的那一个,要求得到的是一个排列。
如果 $a_i \neq b_i$ 那么相当于要在 $a_i, b_i$ 中选择一个,如果 $a_i = b_i$ 那么一定是 $a_i$。
考虑建图 $a_i$ 和 $b_i$ 连一条边,表示要在这两个点中选一个。那么每个连通块需要有 $k$ 个点和 $k$ 条边,即每个连通块为基环树。
如果环为自环方案数为 $n$,否则为 $2$。
Code
#include <bits/stdc++.h> using namespace std; #define ll long long void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int n) { int x, y; exgcd(a, n, x, y); return (x < 0) ? (x + n) : (x); } const int Mod = 998244353; template <const int Mod = :: Mod> class Z { public: int v; Z() : v(0) { } Z(int x) : v(x){ } Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) { int x; return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x)); } friend Z operator - (const Z& a, const Z& b) { int x; return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x)); } friend Z operator * (const Z& a, const Z& b) { return Z(a.v * 1ll * b.v); } friend Z operator ~(const Z& a) { return inv(a.v, Mod); } friend Z operator - (const Z& a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } friend bool operator == (const Z& a, const Z& b) { return a.v == b.v; } }; Z<> qpow(Z<> a, int p) { Z<> rt = Z<>(1), pa = a; for ( ; p; p >>= 1, pa = pa * pa) { if (p & 1) { rt = rt * pa; } } return rt; } typedef Z<> Zi; template <typename T> void pfill(T* pst, T* ped, T val) { for ( ; pst != ped; *(pst++) = val); } const int N = 1e5 + 5; typedef class UnionFound { public: bool loop[N]; int f[N], v[N]; void init(int n) { pfill(loop, loop + n + 1, false); pfill(v + 1, v + n + 1, -1); for (int i = 1; i <= n; i++) { f[i] = i; } } int find(int x) { return f[x] == x ? x : (f[x] = find(f[x])); } void unit(int x, int y) { bool flg = (x == y); x = find(x), y = find(y); if (x ^ y) { f[y] = x; v[x] += v[y]; loop[x] |= loop[y]; } v[x]++; loop[x] |= flg; } bool chk(int p) { return v[find(p)] == 0; } bool has_self_loop(int p) { return loop[find(p)]; } } UnionFound; int T, n; int a[N], b[N]; UnionFound uf; void solve() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", a + i); } for (int i = 1; i <= n; i++) { scanf("%d", b + i); } uf.init(n); for (int i = 1; i <= n; i++) { uf.unit(a[i], b[i]); } for (int i = 1; i <= n; i++) { if (!uf.chk(i)) { puts("0"); return; } } Zi ans = 1; for (int i = 1; i <= n; i++) { if (uf.find(i) == i) { if (uf.loop[i]) { ans = ans * n; } else { ans = ans + ans; } } } printf("%d\n", ans.v); } int main() { scanf("%d", &T); while (T--) { solve(); } return 0; }
Problem E Koxia and Game
只会一个压根儿不能写的套路 dp。sad....
考虑每条边的贡献是两边点数的乘积。并且注意到点数改变只有两种可能。
考虑按顺序操作每条边的过程,每个连通块内部只会被新加入的连接它的边影响,因此只需要记录当前点 $u$ 有蝴蝶的概率就可以了。
Code
#include <bits/stdc++.h> using namespace std; #define ll long long void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int n) { int x, y; exgcd(a, n, x, y); return (x < 0) ? (x + n) : (x); } const int Mod = 998244353; template <const int Mod = :: Mod> class Z { public: int v; Z() : v(0) { } Z(int x) : v(x){ } Z(ll x) : v(x % Mod) { } friend Z operator + (const Z& a, const Z& b) { int x; return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x)); } friend Z operator - (const Z& a, const Z& b) { int x; return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x)); } friend Z operator * (const Z& a, const Z& b) { return Z(a.v * 1ll * b.v); } friend Z operator ~(const Z& a) { return inv(a.v, Mod); } friend Z operator - (const Z& a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } friend bool operator == (const Z& a, const Z& b) { return a.v == b.v; } }; Z<> qpow(Z<> a, int p) { Z<> rt = Z<>(1), pa = a; for ( ; p; p >>= 1, pa = pa * pa) { if (p & 1) { rt = rt * pa; } } return rt; } typedef Z<> Zi; template <typename T> void pfill(T* pst, T* ped, T val) { for ( ; pst != ped; *(pst++) = val); } const int N = 3e5 + 5; int n, K; Zi f[N]; int eu[N], ev[N], sz[N], Fa[N]; vector<int> G[N]; int dfs(int p, int fa) { Fa[p] = fa; for (auto e : G[p]) { if (e ^ fa) { sz[p] += dfs(e, p); } } return sz[p]; } int main() { scanf("%d%d", &n, &K); for (int i = 1, x; i <= K; i++) { scanf("%d", &x); f[x] = sz[x] = 1; } for (int i = 1, u, v; i < n; i++) { scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); eu[i] = u, ev[i] = v; } dfs(1, 0); Zi ans = 0, inv2 = (Mod + 1) >> 1; for (int i = 1, u, v; i < n; i++) { if (Fa[u = eu[i]] == (v = ev[i])) { swap(u, v); } int l = K - sz[v], r = sz[v]; Zi puv = inv2 * f[u] * (1 - f[v]), pvu = inv2 * f[v] * (1 - f[u]); ans += (1 - puv - pvu) * l * r; if (l) ans += puv * (l - 1) * (r + 1); if (r) ans += pvu * (l + 1) * (r - 1); Zi nfu = inv2 * f[u] * (f[v] + 1) + pvu; Zi nfv = inv2 * (f[u] + 1) * f[v] + puv; f[u] = nfu, f[v] = nfv; } ans = ans * ~(Zi(K) * (K - 1) * inv2); printf("%d\n", ans.v); return 0; }
Problem F Koxia and Sequence
只会复杂度大概 1e9 的 dp,麻了。
考虑只要求 $a_i$ 要是 $y$ 的子集怎么算方案数。暴力的话 $\sum_{\sum a_i = x} \prod [a_i 是 y 的子集]$
后面那个用二进制下的组合数来表示 $\binom{y}{a_i}$,然后用生成函数或者组合恒等式容易得到这整个是 $\binom{ny}{x}$
算答案的话,考虑算 $a_i$ 对答案某一二进制位的贡献,由对称性,只用算 $a_1$ 就可以了。
然后相当于硬点 $a_1$ 包含 $2^k$,同样也能用这样的方法算方案数。
Code
#include <bits/stdc++.h> using namespace std; #define ll long long ll n, x, y, ans; int main() { scanf("%lld%lld%lld", &n, &x, &y); for (int s = y; s; s = (s - 1) & y) { // a_1 + \cdots + a_n = x (a_i \subseteq s) // \sum \prod \binom{s}{a_i} // ((1 + t)^s)^n [t^x] // \binom {ns}{x} for (int i = 0; (s >> i); i++) { // 2^i \subseteq a_1 // \sum \binom{s-2^i}{a'_1} \prod \binom{s}{a_i} // \binom{ns - 2^i}{x - 2^i} ll a = n * s - (1 << i), b = x - (1 << i); if (((s >> i) & 1) && a >= 0 && b >= 0 && (b & a) == b) { ans ^= (1 << i); } } } ans *= n & 1; printf("%lld\n", ans); return 0; }
Problem G Koxia and Bracket
好不容易,这个我会!然后:
多项式题还能被卡常的吗?出题人,我*#&¥*&*&*#*&!$(*#
不难发现最优策略是:拿个栈去跑,遇到 ( push,遇到 ) 堆顶有 ( pop,否则 push,然后把栈剩下的里面的东西删掉。
容易证明某个被删掉的 ( 右边一定没有被删掉的 )。因此接下来我们只用考虑删 ) 的情形,剩下我们把序列翻转一下再做一遍就好了。
考虑栈里剩下的每个 ) 相当于是要求这个位置左侧还需要额外删一个。
假设总共要删 $k$ 个,我删掉了 $p_1, p_2, \cdots, p_k$ 处的 ),那么要求 $p_i$ 不能超过第 $i$ 个栈中的 ) 的位置。
朴素 dp 的话 $f_{i, j}$ 表示考虑到 $i$ 已经删了 $j$ 个,转移到 $f_{i + 1, j}$ 或者 $f_{i + 1, j + 1}$。
相当于是在网格图里向右或者向右上走一格,这个右上非常地不优美,把第 $k$ 行向左推 $k - 1$ 格就变成向右或者向上走一格。
然后就变成在一个梯形的网格图上路径计数。
注意到如果是一个优美的矩形,算方案数显然能直接卷积。
所以考虑分治
对于矩形部分用卷积算,剩下两块阴影部分分别拿去递归。
因为矩形算卷积是已知刚跨过左边界和下边界上每条边的方案数,然后算刚跨过右边界和上边界上每条边的方案数,要做 4 次卷积,就被卡了。当矩形长宽不大的时候,直接把这部分换成平方 dp 就能过了。
题解做法的话是记录额外删了多少个,限制的话变成要求额外删的数量总是大于等于 0 的。
假如现在需要快速转移 $[l, r]$,假设这里面有 $num$ 个需要被删的 ),那么当第二维超过 $num$ 的时候转移不会有任何特殊影响,直接用卷积算出对 $r + 1$ 的贡献就可以了。
所以一个区间实际需要在内部维护的 dp 第二维只和这里面需要被删的 ) 有关。因此直接分治就好了。
Code
#include <bits/stdc++.h> using namespace std; #define g __ntt_g #ifdef local #define _assert(expr) assert(expr) #else #define _assert(expr) #endif const int Mod = 998244353; const int bzmax = 20; const int N = 1 << bzmax; const int g = 3; typedef long long ll; void exgcd(int a, int b, int& x, int& y) { if (!b) { x = 1, y = 0; } else { exgcd(b, a % b, y, x); y -= (a / b) * x; } } int inv(int a, int Mod = ::Mod) { int x, y; exgcd(a, Mod, x, y); return (x < 0) ? (x + Mod) : x; } template <const int Mod = ::Mod> class Z { public: int v; Z() { } Z(int v) : v(v) { _assert(v >= 0 && v < Mod); } Z(ll x) : v(x % Mod) { } friend Z operator + (Z a, Z b) { int x; return Z((x = a.v + b.v) >= Mod ? x - Mod : x); } friend Z operator - (Z a, Z b) { int x; return Z((x = a.v - b.v) < 0 ? x + Mod : x); } friend Z operator * (Z a, Z b) { return 1ll * a.v * b.v; } friend Z operator ~ (Z a) { _assert(a.v); return inv(a.v); } friend Z operator - (Z a) { return Z(0) - a; } Z& operator += (Z b) { return *this = *this + b; } Z& operator -= (Z b) { return *this = *this - b; } Z& operator *= (Z b) { return *this = *this * b; } }; typedef Z<> Zi; Zi qpow(Zi a, int p) { if (p < 0) p += Mod - 1; Zi rt = 1; for ( ; p; p >>= 1, a *= a) { if (p & 1) { rt *= a; } } return rt; } Zi qpow(Zi a, ll p) { return qpow(a, (int) (p % (Mod - 1))); } class NTT { private: Zi gn[bzmax + 1], _gn[bzmax + 1]; Zi Wn[2][N << 1]; public: NTT() { int phi = Mod - 1; for (int i = 0; i <= bzmax; i++) { gn[i] = qpow(g, (phi >> i)); _gn[i] = ~gn[i]; } Zi *cw = Wn[0]; for (int i = 1; i <= bzmax; i++) { *(cw++) = 1; for (int j = (1 << (i - 1)) - 1; j--; cw++) { *cw = *(cw - 1) * gn[i]; } } cw = Wn[1]; for (int i = 1; i <= bzmax; i++) { *(cw++) = 1; for (int j = (1 << (i - 1)) - 1; j--; cw++) { *cw = *(cw - 1) * _gn[i]; } } } void operator () (Zi* f, int len, bool rev) { _assert(len <= N); for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) { if (i < j) swap(f[i], f[j]); for (k = len >> 1; j >= k; j -= k, k >>= 1); } Zi *wn = Wn[rev], *cw, a, b, *x, *y; for (int l = 2, hl; l <= len; l <<= 1) { hl = l >> 1, cw = wn; for (int i = 0; i < len; i += l) { wn = cw, x = f + i, y = x + hl; for (int j = hl; j--; ) { a = *x, b = *y * *(wn++); *(x++) = a + b, *(y++) = a - b; } } } if (rev) { a = ~Zi(len); for ( ; len--; *(f++) *= a); } } } NTT; #define dft(f, len) NTT(f, len, false) #define idft(f, len) NTT(f, len, true) #define clr(f, len) memset(f, 0, (len) << 2) int get_dft_length(int len) { int rt = 1; while (rt < len) rt <<= 1; return rt; } #define get_length(_x) get_dft_length(_x) #undef g template <typename T1, typename T2> void do_add(T1* a, const T2 * b, int n) { while (n--) *(a++) += *(b++); } template <typename T1, typename T2> void do_sub(T1* a, const T2 * b, int n) { while (n--) *(a++) -= *(b++); } template <typename T1, typename T2> void do_mul(T1* a, const T2 * b, int n) { while (n--) *(a++) *= *(b++); } namespace poly_temporary { Zi ta[N], tb[N]; } void poly_inverse(const Zi *f, Zi* g, int n) { using namespace poly_temporary; static Zi A[N]; if (n > 64) { // if (n > 1) { int hn = (n + 1) >> 1; int t = get_length(hn * 3); poly_inverse(f, g, hn); copy(f, f + n, A); clr(A + n, t - n); copy(g, g + hn, ta); dft(g, t); dft(A, t); for (int i = 0; i < t; i++) { g[i] = g[i] * (Zi(2) - g[i] * A[i]); } idft(g, t); copy(ta, ta + hn, g); clr(g + n, t - n); } else { g[0] = ~f[0]; for (int i = 1; i < n; i++) { g[i] = 0; for (int j = 1; j <= i; j++) { g[i] -= f[j] * g[i - j]; } g[i] *= g[0]; } } } vector<Zi> fac, _fac, Inv; void init_fac(int n) { fac.resize(n + 1); _fac.resize(n + 1); fac[0] = 1; for (int i = 1; i <= n; i++) { fac[i] = fac[i - 1] * i; } _fac[n] = ~fac[n]; for (int i = n; i; i--) { _fac[i - 1] = _fac[i] * i; } } void init_inv(int n) { Inv.resize(n + 1); Inv[0] = 0, Inv[1] = 1; for (int i = 2; i <= n; i++) { Inv[i] = -Inv[Mod % i] * (Mod / i); } } typedef class Poly : public vector<Zi> { public: using vector<Zi>::vector; Poly& fix(int sz) { this->resize(sz, 0); return *this; } Zi eval(Zi x) const { Zi rt = 0; const Zi *f = data(); for (int i = size(); i; rt = rt * x + f[--i]); return rt; } vector<Zi> eval(Zi* x, int n); // TODO vector<Zi> eval(vector<Zi> x) { return eval(x.data(), x.size()); } void shrink() { while (size() > 1u && !back().v) pop_back(); } Poly deri(); Poly integ(); Poly& do_deri(); Poly& do_integ(); } Poly; ostream& operator << (ostream& os, const Poly& f) { bool first = true; os << "{"; for (auto x : f) { if (first) { first = false; } else { os << ", "; } os << x.v; } os << "}"; return os; } Poly operator + (const Poly& a, const Poly& b) { Poly rt = a; int n = max(a.size(), b.size()); rt.resize(n); for (int i = b.size(); i--; ) rt[i] += b[i]; return rt; } Poly operator - (const Poly& a, const Poly& b) { Poly rt = a; int n = max(a.size(), b.size()); rt.resize(n); for (int i = b.size(); i--; ) rt[i] -= b[i]; return rt; } Poly operator * (Poly a, Poly b) { int n = a.size(), m = b.size(), k = n + m - 1; if (n < 64 || m < 64) { Poly rt (k, Zi(0)); for (int i = a.size(); i--; ) { for (int j = b.size(); j--; ) { rt[i + j] += a[i] * b[j]; } } return rt; } int t = get_length(k); a.resize(t, Zi(0)); b.resize(t, Zi(0)); dft(a.data(), t); dft(b.data(), t); do_mul(a.data(), b.data(), t); idft(a.data(), t); return a.fix(k); } Poly operator ~ (Poly b) { int n = b.size(), t = get_length((n << 1) + (n & 1)); Poly rt (t, Zi(0)); poly_inverse(b.data(), rt.data(), n); return rt.fix(n); } Poly operator / (Poly a, Poly b) { int n = a.size(), m = b.size(); if (n < m) return Poly {0}; int d = n - m + 1; reverse(a.begin(), a.end()); reverse(b.begin(), b.end()); a.resize(d, Zi(0)); b.resize(d, Zi(0)); a = (a * ~b).fix(d); reverse(a.begin(), a.end()); return a; } Poly operator % (Poly a, Poly b) { int n = a.size(), m = b.size(); if (n < m) return a; if (!m) return Poly{0}; return (a - a / b * b).fix(m - 1); } pair<Poly, Poly> divide(Poly a, Poly b) { int n = a.size(), m = b.size(); if (n < m) return make_pair(Poly{0}, a); if (!m) return make_pair(a / b, Poly{0}); Poly d = a / b; return make_pair(d, (a - d * b).fix(m - 1)); } Poly Poly::deri() { Poly b = *this; return b.do_deri(); } Poly& Poly::do_deri() { shrink(); Zi* f = data(); int sz = size(); for (int i = 0; i < sz - 1; i++) { f[i] = f[i + 1] * (i + 1); } f[sz - 1] = 0; if (sz > 1) { pop_back(); } return *this; } Poly Poly::integ() { Poly b = *this; return b.do_integ(); } Poly& Poly::do_integ() { push_back(0); Zi* f = data(); int sz = size(); _assert(sz < (signed) Inv.size()); for (int i = sz; --i; ) { f[i] = f[i - 1] * Inv[i]; } f[0] = 0; return *this; } Poly ln(Poly a) { int n = a.size(); _assert(!a.empty() && a[0].v == 1); return (~a * a.deri()).fix(n - 1).integ(); } void _exp(Poly& f, Poly& g, int n) { // using namespace poly_temporary; if (n == 1) { g = Poly{1}; } else { int hn = (n + 1) >> 1; _exp(f, g, hn); Poly gn = g; Poly h = Poly(f.begin(), f.begin() + n) - ln(gn.fix(n)); h.erase(h.begin(), h.begin() + hn); h = g * h; g.resize(n); for (int i = hn; i < n; i++) { g[i] = h[i - hn]; } } } Poly exp(Poly a) { _assert(a[0].v == 0); Poly h; _exp(a, h, a.size()); return h; } #undef clr #undef get_length int L; char s[N]; void do_reverse() { char *p = s; for (; *p; p++) { *p = "()"[*p == '(']; } reverse(s, p); } Poly subpoly(Poly& f, int l, int r) { Poly g (r - l); for (int i = l; i < r; i++) { g[i - l] = f[i]; } return g; } Poly revpoly(Poly f) { reverse(f.begin(), f.end()); return f; } Poly eval_cross(Poly f, int m) { int n = f.size(); for (int i = 0; i < n; i++) { f[i] *= _fac[n - 1 - i]; } Poly g (n + m - 1); for (int i = 0; i < (signed) g.size(); i++) { g[i] = fac[i]; } f = f * g; g.resize(m); for (int i = 0; i < m; i++) { g[i] = f[i + n - 1] * _fac[i]; } return g; } Poly eval_paral(Poly f, int m) { int n = f.size(); Poly g (n); for (int i = 0; i < n; i++) { g[i] = fac[i + m] * _fac[i] * _fac[m]; } (f = f * g).resize(n); return f; } Poly join(Poly f, Poly g) { int n = f.size(), m = g.size(); f.resize(n + m); for (int i = 0; i < m; i++) { f[i + n] = g[i]; } return f; } vector<pair<int, int>> ps; Poly solve(int pl, int pr, Poly f) { if (pl == pr) { return eval_cross(f, ps[pr + 1].first - ps[pl].first); } int mid = (pl + pr) >> 1; int wid = ps[mid + 1].first - ps[pl].first; int hei = ps[pr + 1].second - ps[mid + 1].second; Poly h = solve(pl, mid, subpoly(f, 0, ps[mid + 1].second - ps[pl].second)); Poly nf, R0; f = subpoly(f, ps[mid + 1].second - ps[pl].second, f.size()); if (wid < 256 || hei < 256) { nf.resize(hei, 0); R0.resize(wid, 0); for (int i = 0; i < hei; i++) { for (int j = 1; j < wid; j++) { h[j] += h[j - 1]; } nf[i] += h.back(); } R0 = h; for (int i = 0; i < wid; i++) { for (int j = 1; j < hei; j++) { f[j] += f[j - 1]; } R0[i] += f.back(); } nf = nf + f; } else { nf = eval_cross(h, hei) + eval_paral(f, wid - 1); R0 = eval_cross(f, wid) + eval_paral(h, hei - 1); } Poly R1 = solve(mid + 1, pr, nf); return join(R0, R1); } Zi solve() { vector<int> pos; int tp = 0; int cnt = 0; for (char *p = s; *p; p++) { if (*p == '(') { tp++; } else { cnt++; !tp ? (pos.push_back(cnt), 0) : tp--; } } if (pos.empty()) { return 1; } for (int i = 1; i < (signed) pos.size(); pos[i] -= i, i++); ps.clear(); ps.emplace_back(0, 1); for (int i = 1; i < (signed) pos.size(); i++) { if (pos[i] != pos[i - 1]) { ps.emplace_back(pos[i - 1], i + 1); } } ps.emplace_back(pos.back(), (int) pos.size() + 1); Poly fi (pos.size(), 0); fi[0] = 1; Poly f = solve(0, (signed) ps.size() - 2, fi); Zi ret = 0; for (auto x : f) { ret += x; } return ret; } int main() { scanf("%s", s); int len_s = strlen(s); init_fac(len_s + 1); Zi a = solve(); do_reverse(); Zi b = solve(); printf("%d", (a * b).v); return 0; }
Problem H Koxia, Mahiru and Winter Festival
别催了,别催了,在路上了.jpg