luogu P7044 「MCOI-03」括号

https://www.luogu.com.cn/problem/P7044

首先考虑 k = 0 k=0 k=0怎么做
显然最后括号剩下的一定是 . . . ) ) ) ( ( ( . . . ...)))(((... ...)))(((...
答案记为 f ( l , r ) f(l, r) f(l,r)

考虑每个右括号往左第一个匹配到的左括号记为 p r e [ x ] pre[x] pre[x]

然后考虑扫描线, r r r每向右移动一个位置 p r e [ x + 1 ] pre[x+1] pre[x+1]及之前到括号匹配数为+1,对应的 f f f会-1
p r e [ x ] pre[x] pre[x]之后的 f f f会+1

然后在考虑 k > 0 k>0 k>0的情况
容易发现,其实就是 ∑ l − 1 n ∑ r = l n ( l − 1 + k − 1 k − 1 ) ( n − r + k − 1 k − 1 ) f ( l , r ) = ∑ l − 1 n ( l − 1 + k − 1 k − 1 ) ∑ r = l n ( n − r + k − 1 k − 1 ) f ( l , r ) \sum_{l-1}^n\sum_{r=l}^n \binom{l-1 +k-1}{k-1}\binom{n-r+k-1}{k-1}f(l,r)\\=\sum_{l-1}^n \binom{l-1 +k-1}{k-1}\sum_{r=l}^n\binom{n-r+k-1}{k-1}f(l,r) l1nr=ln(k1l1+k1)(k1nr+k1)f(l,r)=l1n(k1l1+k1)r=ln(k1nr+k1)f(l,r)
按照上面的扫描线做法类似维护即可

code:

#include<bits/stdc++.h>
#define N 2000050
#define mod 998244353
#define ll long long
using namespace std;
ll qpow(ll x, ll y) {
    ll ret = 1;
    for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
    return ret;
}
ll fac[N], ifac[N];
void init(int n) {
    fac[0] = 1;
    for(int i = 1; i <= n; i ++) fac[i] = fac[i - 1] * i % mod;
    ifac[n] = qpow(fac[n], mod - 2);
    for(int i = n - 1; i >= 0; i --) ifac[i] = ifac[i + 1] * (i + 1) % mod;
} 
ll C(int n, int m) {
    return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
int n, k, a[N], sta[N], top, pre[N];
char st[N];
int main() {
    scanf("%d%d", &n, &k);
    init(n + k);
    scanf("%s", st + 1);
    for(int i = n; i >= 1; i --) {
        if(st[i] == ')') sta[++ top] = i;
        else if(top) pre[sta[top --]] = i;
    }

    ll ans = 0, gs = 0;
    for(int i = 1; i <= n; i ++) { 
        a[i] = (a[i - 1] + C(i + k - 2, k - 1)) % mod;
        gs = (gs + a[i] - 2ll * a[pre[i]] % mod + mod) % mod;
        ans = (ans + gs * C(n - i + k - 1, k - 1) % mod) % mod;
    }
    printf("%lld", ans);
    return 0;
}
posted @ 2021-10-29 08:38  lahlah  阅读(36)  评论(0编辑  收藏  举报