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)
l−1∑nr=l∑n(k−1l−1+k−1)(k−1n−r+k−1)f(l,r)=l−1∑n(k−1l−1+k−1)r=l∑n(k−1n−r+k−1)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;
}