Top-Notch Insertions(思维/组合/线段树)

题目
题意:给定一个插入排序的排序过程,求有多少数组满足满足该过程,使得最终得到的排序数组是有许的。其中数组规模为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<=...<=an1<=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+1i+1,i+2,..,n1。那么最后得到的数组,必然有 a 1 ∗ < a 2 ∗ < . . . < a n − 1 ∗ < a n ∗ {a_1}^*<{a_2}^*<...<{a_{n-1}}^*<{a_n}^* a1<a2<...<an1<an,此时 a n ∗ {a_n}^* an最大取值为 n + ( n − 1 − c ) n+(n-1-c) n+(n1c)(因为总共有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+n1cn
如何确定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);
		}
	}
	
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值