题意很清楚,就不复述了。
不难发现,我们首先要求出场景数小于给定 galgame 的 galgame 数量,于是我们需要求出场景数 \(=i\) 的 galgame 数量,设为 \(f_i\)。
考虑根节点,当 A 场景大小为 \(j\) 时,B 场景的大小为 \(i-j-1\)。枚举每个 \(j\),不难得到 \(f_i=\sum\limits_{j=0}^{i-1}f_j\times f_{i-j-1}\),显然这就是卡特兰数的递推式。对于卡特兰数我们有通项公式 \(f_i=\dfrac{\binom{2i}{i}}{i+1}=\dfrac{(2i)!}{i!\,(i+1)!}\),因此我们可以 \(O(n)\) 预处理出每个卡特兰数。
接下来考虑场景数相等的情况,这种情况也可以分为两种:A 场景不同,A 场景相同。
对于 A 场景相同的情况,直接完全交给 B 场景变为子问题即可。
对于 A 场景不同的情况,B 场景可以是任何结构。设 \(m\) 为 A 场景的大小,先考虑大小 \(<m\) 的,这部分贡献是 \(\sum\limits_{j=0}^{m-1}f_j\times f_{n-i-1}\);对于大小 \(=m\) 的情况,递归变成子问题,再乘上 B 场景的情况数即可。
尴尬的是这种方法是 \(O(n^2)\) 的,无法通过本题。
我们发现瓶颈在于 \(\sum\limits_{j=0}^{m-1}f_j\times f_{n-i-1}\),这个东西就是卡特兰数的递推式少了几项。不难发现 \(\sum\limits_{j=0}^{m-1}f_j\times f_{n-i-1}=f_n-\sum\limits_{j=0}^{n-m}f_j\times f_{n-i-1}\),于是每个子问题的操作次数都可以控制在 \(\left\lceil\dfrac{n}{2}\right\rceil\) 以内(\(n\) 为子任务规模),使用启发式合并,时间复杂度降到 \(O(n\log n)\)。
代码:
#include<bits/stdc++.h>
#define endl '\n'
#define rep(i, s, e) for(int i = s, i##E = e; i <= i##E; ++i)
#define per(i, s, e) for(int i = s, i##E = e; i >= i##E; --i)
#define F first
#define S second
#define int ll
#define gmin(x, y) (x = min(x, y))
#define gmax(x, y) (x = max(x, y))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double f128;
typedef pair<int, int> pii;
constexpr int N = 1e6 + 5, p = 998244353;
int n, frac[N * 2], inv[N * 2], h[N], s[N], sz[N], ans;
pii a[N];
int qp(int a, int b) {
int res = 1;
for(; b; b >>= 1, a = a * a % p)
if(b & 1) res = res * a % p;
return res;
}
void init() {
cin >> n;
rep(i, 1, n) cin >> a[i].F >> a[i].S;
frac[0] = 1;
rep(i, 1, n * 2) frac[i] = frac[i - 1] * i % p; // 预处理阶乘
inv[n * 2] = qp(frac[n * 2], p - 2);
per(i, n * 2 - 1, 0) inv[i] = inv[i + 1] * (i + 1) % p; // 预处理阶乘的逆元
rep(i, 0, n) h[i] = frac[i * 2] * inv[i] % p * inv[i + 1] % p; // 预处理卡特兰数
}
void dfs(int u) { // 预处理子树大小
sz[u] = 1;
if(a[u].F) dfs(a[u].F), sz[u] += sz[a[u].F];
if(a[u].S) dfs(a[u].S), sz[u] += sz[a[u].S];
}
int solve(int u) {
int ans = 0;
if(a[u].F) {
if(sz[a[u].F] <= sz[a[u].S]) { // 启发式合并
rep(i, 0, sz[a[u].F] - 1)
(ans += h[i] * h[sz[u] - i - 1]) %= p;
}
else {
ans = (ans + h[sz[u]]) % p;
rep(i, 0, sz[a[u].S])
ans = ((ans - h[i] * h[sz[u] - i - 1]) % p + p) % p;
}
(ans += solve(a[u].F) * h[sz[a[u].S]]) %= p; // 递归变成子问题
}
if(a[u].S) (ans += solve(a[u].S)) %= p;
return ans;
}
signed main() {
#ifdef ONLINE_JUDGE
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
#endif
init();
dfs(1);
rep(i, 1, n - 1) (ans += h[i]) %= p;
cout << (ans + solve(1)) % p << endl;
return 0;
}
标签:sz,场景,frac,int,Luogu,P7118,inv,define
From: https://www.cnblogs.com/untitled0/p/luogu-p7118.html