这是一个很菜的 Oier 的博客|

Hanx16Msgr

园龄:2年8个月粉丝:12关注:3

CF1830C Hyperregular Bracket Strings

Hyperregular Bracket Strings

Luogu CF1830C

题目描述

给定一个数 nk 个区间 [li,ri][1,n]

我们定义,对于一个长度为 n 的,仅由 () 组成的合法括号序列,如果它的每一个区间 [li,ri] 内的子串都是合法括号序列,那么这个括号序列是好的

好的括号序列的数量,答案对 998244353 取模。

Solution

先考虑所有区间两两不交的情况。发现如果保证所有区间内的括号序列合法,那么对于所有不在区间内的位置组成的括号序列也会是合法的。比如 ()(()(()))[4,5] 是合法的,那么 [1,3][6,10] 也是合法的。那么每个区间和剩余部分都是相互独立的。一个长度为 2m 的括号序列个数为卡特兰数 (2mm)(2mm1)。那么最后答案就是所有区间的括号序列个数的乘积。

但是此处区间可能存在交。只考虑两个相交的区间看有什么性质。发现两个区间 [l,r],[a,b](ra,la),可以看作是三个不交的区间 [l,a),[a,r),[r,b] 的组合。这样就把所有区间全部拆成不交的了。如果直接枚举相交的区间显然不行,考虑给每个区间随机一个权值 v,然后将这个区间内的所有位置都异或上 v,最后如果两个位置权值相同那么覆盖这两个位置的区间集合也是相同的。因此统计每个权值出现的次数然后用卡特兰数乘起来即可。

区间异或使用差分,时间复杂度 O(n+k)

Code
mint fac[_N], ifac[_N];
void init(int n) {
    fac[0] = 1; For(i, 1, n) fac[i] = fac[i-1] * i;
    ifac[n] = 1 / fac[n]; Rof(i, n, 1) ifac[i-1] = ifac[i] * i;
}
mint binom(int x, int y) {
    return x < y || y < 0 ? 0 : fac[x] * ifac[y] * ifac[x-y];
}
mint cat(int n) {
    if (n & 1) return 0;
    return binom(n, n / 2) - binom(n, n / 2 - 1);
}
mt19937_64 rnd(9);
signed main() {
    cin.tie(0)->sync_with_stdio(0);
    int T; cin >> T;
    init(3e5);
    while (T--) {
        int N, M; cin >> N >> M;
        vector<u64> val(N + 2, 0);
        For(i, 1, M) {
            int l, r; cin >> l >> r;
            u64 v = rnd();
            val[l] ^= v, val[r+1] ^= v;
        }
        unordered_map<u64, int> mp;
        For(i, 1, N) val[i] ^= val[i-1], ++mp[val[i]];
        mint ans = 1;
        for (auto& pr: mp) ans *= cat(pr.second);
        cout << ans << '\n';
    }
}
posted @   Hanx16Msgr  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起