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
浙公网安备 33010602011771号