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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章