Luogu P7118

题面

题意很清楚,就不复述了。

不难发现,我们首先要求出场景数小于给定 galgame 的 galgame 数量,于是我们需要求出场景数 =i 的 galgame 数量,设为 fi

考虑根节点,当 A 场景大小为 j 时,B 场景的大小为 ij1。枚举每个 j,不难得到 fi=j=0i1fj×fij1,显然这就是卡特兰数的递推式。对于卡特兰数我们有通项公式 fi=(2ii)i+1=(2i)!i!(i+1)!,因此我们可以 O(n) 预处理出每个卡特兰数。

接下来考虑场景数相等的情况,这种情况也可以分为两种:A 场景不同,A 场景相同。

对于 A 场景相同的情况,直接完全交给 B 场景变为子问题即可。

对于 A 场景不同的情况,B 场景可以是任何结构。设 m 为 A 场景的大小,先考虑大小 <m 的,这部分贡献是 j=0m1fj×fni1;对于大小 =m 的情况,递归变成子问题,再乘上 B 场景的情况数即可。

尴尬的是这种方法是 O(n2) 的,无法通过本题。

我们发现瓶颈在于 j=0m1fj×fni1,这个东西就是卡特兰数的递推式少了几项。不难发现 j=0m1fj×fni1=fnj=0nmfj×fni1,于是每个子问题的操作次数都可以控制在 n2 以内(n 为子任务规模),使用启发式合并,时间复杂度降到 O(nlogn)

代码:

#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;
}
posted @   untitled0  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示