题目
题意:给定一个插入排序的排序过程,求有多少数组满足满足该过程,使得最终得到的排序数组是有许的。其中数组规模为n,有m次插入,每次插入将
x
i
x_i
xi上的数组插入到
x
i
x_i
xi中。数组中每个元素的取值范围为
[
1
,
n
]
[1,n]
[1,n]。
官方题解
参考题解
思路:
对于一个有序的数组
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an,有
a
1
<
=
a
2
<
=
.
.
.
<
=
a
n
−
1
<
=
a
n
a_1<=a_2<=...<=a_{n-1}<=a_n
a1<=a2<=...<=an−1<=an,现在把情况细分为<和<=(也就是==),那么对于
1
<
=
i
<
n
1<=i<n
1<=i<n,有
a
i
<
=
a
i
+
1
a_i<=a_{i+1}
ai<=ai+1或
a
i
<
a
i
+
1
a_i<a_{i+1}
ai<ai+1。设
c
=
n
u
m
a
i
<
a
i
+
1
c=num_{a_i<a_{i+1}}
c=numai<ai+1,
对
于
每
个
a
i
<
=
a
i
+
1
,
我
们
把
i
+
1
,
i
+
2
,
.
.
,
n
的
元
素
都
加
1
‾
\underline{对于每个a_i<=a_{i+1},我们把i+1,i+2,..,n的元素都加1}
对于每个ai<=ai+1,我们把i+1,i+2,..,n的元素都加1。那么最后得到的数组,必然有
a
1
∗
<
a
2
∗
<
.
.
.
<
a
n
−
1
∗
<
a
n
∗
{a_1}^*<{a_2}^*<...<{a_{n-1}}^*<{a_n}^*
a1∗<a2∗<...<an−1∗<an∗,此时
a
n
∗
{a_n}^*
an∗最大取值为
n
+
(
n
−
1
−
c
)
n+(n-1-c)
n+(n−1−c)(因为总共有n-1个比较关系)。
此时,我们发现,通过加1操作,我们把原始数组
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an与新数组
a
1
∗
,
a
2
∗
,
.
.
.
a
n
∗
{a_1}^*,{a_2}^*,...{a_n}^*
a1∗,a2∗,...an∗做了完美的一对一映射。给定c,要求数组
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an的个数,等价于求数组
a
1
∗
,
a
2
∗
,
.
.
.
a
n
∗
{a_1}^*,{a_2}^*,...{a_n}^*
a1∗,a2∗,...an∗的个数,而该个数显然就是
C
n
+
n
−
1
−
c
n
C_{n+n-1-c}^n
Cn+n−1−cn。
如何确定c呢?
从后往前来看这m次插入,每次插入将元素从位置
x
i
x_i
xi移动到
y
i
y_i
yi,那么有
y
i
+
1
y_{i+1}
yi+1上的元素(不妨设为q)大于当前元素(不妨设为p)。但我们需要检查下是否存在元素w,使得w<q,如果不存在,才能对c+1。如下例子,存在元素w(第5个元素)<q(第6个元素),虽然已知p(第4个元素)<q,但p和q不相邻,所以c这时不能+1。
1 1 1 1 0 1
判断元素是否存在,可以用线段树区间和来维护,初始时每个位置的值都为1。
注意由于有多组样例,题目没有保证n的和范围。我们可以把线段树在修改后回滚下,使其更通用。
#include<bits/stdc++.h>
using namespace std;
#define inf 1000000000
const int maxn = 200001;
#define lson (rt << 1)
#define rson (rt << 1 | 1)
const int mod = 998244353;
int n, m;
int sum[maxn<<2];
int y[maxn], x;
int vis[maxn];
int fac[maxn<<1], rfac[maxn<<1];
int mul(int a, int b) {
return 1LL * a * b % mod;
}
int qpow(int a, int q) {
int res = 1;
while (q) {
if (q & 1) res = mul(res, a);
q >>= 1;
a = mul(a, a);
}
return res;
}
void init() {
fac[0] = 1;
int n = maxn * 2;
for (int i = 1; i < n; ++i) {
fac[i] = mul(fac[i-1], i);
}
rfac[n - 1] = qpow(fac[n - 1], mod - 2);
for (int i = n - 2; i >= 0; --i) {
rfac[i] = mul(rfac[i+1], i + 1);
}
}
void pushup(int rt) {
sum[rt] = sum[lson] + sum[rson];
}
void build(int rt, int l, int r) {
if (l == r) {
sum[rt] = 1;
return;
}
int mid = (l + r) / 2;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(rt);
}
int query(int rt, int l, int r, int val) {
if (l == r) {
return l;
}
int mid = (l + r) >> 1;
if (val <= sum[lson]) return query(lson, l, mid, val);
return query(rson, mid + 1, r, val - sum[lson]);
}
void update(int rt, int l, int r, int pos, int val) {
if (l == r) {
sum[rt] = val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) update(lson, l, mid, pos, val);
else update(rson, mid + 1, r, pos, val);
pushup(rt);
}
int C(int a, int b) {
return mul(mul(fac[a], rfac[b]), rfac[a-b]);
}
int main() {
int t;
scanf("%d", &t);
init();
int len = maxn - 1;
build(1, 1, len);
while (t--) {
scanf("%d%d", &n, &m);
for (int i = 0; i < m; ++i) {
scanf("%d%d", &x, &y[i]);
}
memset(vis, 0, sizeof(vis));
int c = 0;
vector <int> ve;
for (int i = m - 1; i >= 0; --i) {
int p = query(1, 1, len, y[i]);
int q = query(1, 1, len, y[i] + 1);
if (!vis[q]) {
vis[q] = 1;
++c;
}
update(1, 1, len, p, 0);
ve.push_back(p);
}
// printf("c:%d\n", c);
printf("%d\n", C(2 * n - c - 1, n));
for (auto u : ve) {
update(1, 1, len, u, 1);
}
}
}