【YBT2022寒假Day5 A】序列计数(组合数)(莫队)
序列计数
题目链接:YBT2022寒假Day5 A
题目大意
多组询问,每次给你 l,r,x,问你有多少个序列满足序列长度 m 在 l,r 之间,且序列是一个单调上升序列,最小值大于等于 1,最大值小于等于 x。
思路
我们其实会发现它这个问题就是求 \(\sum\limits_{i=l}^rC_x^i\)。
(毕竟它就相当于你在 \(1\sim x\) 中选 \(l\sim r\) 个数,然后排序一下就是一个方案)
然后这个我们先前缀和:\(\sum\limits_{i=1}^rC_{x}^i-\sum\limits_{i-1}^{l-1}C_{x}^i\)
然后问题就变成了求 \(G_x^i=\sum\limits_{i=1}^yC_x^i\)
然后这个感觉就很像可以离线做的那种。
然后你会想到莫队,试着看看它能不能搞。
然后你就会发现这两个东西:
\(G_{x}^y=G_{x}^{y-1}+C_x^y\)(这个显然)
\(G_x^y=2G_{x-1}^y-C_{x-1}^y\)(大概就是你把 \(G_x^y\) 里面的每个 \(C_x^i\) 都拆成 \(C_{x-1}^i+C_{x-1}^{i-1}\))
然后你就可以用莫队搞了。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
#define mo 998244353
using namespace std;
struct node {
int l, r, x;
}q[200001];
struct Ask {
int n, m, pl, op;
}qq[400001];
int T, tot, K;
ll jc[200001], inv[200001], now, inv2;
ll ans[200001];
bool cmp(Ask x, Ask y) {
return ((x.n - 1) / K == (y.n - 1) / K) ? x.m < y.m : x.n < y.n;
}
ll C(ll n, ll m) {
return jc[n] * inv[m] % mo * inv[n - m] % mo;
}
int main() {
// freopen("sequence.in", "r", stdin);
// freopen("sequence.out", "w", stdout);
scanf("%d", &T);
for (int i = 1; i <= T; i++) {
scanf("%d %d %d", &q[i].l, &q[i].r, &q[i].x);
qq[++tot] = (Ask){q[i].x, min(q[i].x, q[i].r), i, 1};
qq[++tot] = (Ask){q[i].x, min(q[i].x, q[i].l - 1), i, mo - 1};
}
jc[0] = 1; for (int i = 1; i <= 200000; i++) jc[i] = jc[i - 1] * i % mo;
inv[0] = inv[1] = 1; for (int i = 2; i <= 200000; i++) inv[i] = inv[mo % i] * (mo - mo / i) % mo;
inv2 = inv[2]; for (int i = 1; i <= 200000; i++) inv[i] = inv[i - 1] * inv[i] % mo;
K = 491;
sort(qq + 1, qq + tot + 1, cmp);
int L = 1, R = 0; now = 1;
for (int i = 1; i <= tot; i++) {
while (L < qq[i].n) now = (now * 2 - C(L, R) + mo) % mo, L++;
while (L > qq[i].n) L--, now = (now + C(L, R)) * inv2 % mo;
while (R < qq[i].m) now = (now + C(L, R + 1)) % mo, R++;
while (R > qq[i].m) R--, now = (now - C(L, R + 1) + mo) % mo;
ans[qq[i].pl] = (ans[qq[i].pl] + now * qq[i].op % mo) % mo;
}
for (int i = 1; i <= T; i++) printf("%lld\n", ans[i]);
return 0;
}