C. The Third Problem(组合数学/思维)

题目

题意

给定一个排列p,定义相似排列p2,如果排列p2为p的相似排列,那么它满足:
对于任意的下标1<=i<=j<=n,有 m e x ( p i , . . . , p j ) = m e x ( p 2 i , . . . , p 2 j ) mex(p_i,...,p_j)=mex(p2_i,...,p2_j) mex(pi,...,pj)=mex(p2i,...,p2j)
这里长度为n的排列,是从数0开始,到数n-1结束。
其中mex函数,表示第一个不存在于集合的、最小的、非负数。
如mex(0,2)=1,mex(2,5,8)=0,mex(0,1,2,3)=4。

现在给定排列p,求它的相似排列有多少个,答案对1e9+7取模。

思路

注意观察,我们这里mex函数的值,第一个不存在于集合的、最小的、非负数。
我们要保证好两个排列,任意子序列的mex相等,实际上只需要保证
集合(0,1,…,x)对应在排列p和排列p2的区间分布是完全一致的,即可,其中(0<=x<=n-1)。

为啥?我们给个简单的证明。
对于任意子区间[i,j],假设 [ p i , . . . , p j ] [p_i,...,p_j] [pi,...,pj]包含元素0,1,…,k,不包含元素k+1。
根据前面的保证,由于 [ p i , . . . , p j ] [p_i,...,p_j] [pi,...,pj]包含元素0,1,…,k,那么 [ p 2 i , . . . , p 2 j ] [p2_i,...,p2_j] [p2i,...,p2j]也包含元素0,1,…,k。
同时,假设 [ p 2 i , . . . , p 2 j ] [p2_i,...,p2_j] [p2i,...,p2j]包含元素k+1,那么 [ p i , . . . , p j ] [p_i,...,p_j] [pi,...,pj]也包含元素k+1,与我们的假设违背,因此 [ p 2 i , . . . , p 2 j ] [p2_i,...,p2_j] [p2i,...,p2j]也不包含元素k+1。
综上,p和p2在子区间[i,j],对应的mex值相等,即 m e x ( p i , . . . , p j ) = m e x ( p 2 i , . . . , p 2 j ) mex(p_i,...,p_j)=mex(p2_i,...,p2_j) mex(pi,...,pj)=mex(p2i,...,p2j)

因此,我们只需从小到大枚举元素,以及当前的左右边界,如果新增元素的位置超出当前的左右边界,则更新左右边界;如果新增元素的位置没超出当前的左右边界,则我们可以在该左右边界的空缺的坑中随意填充该新增的元素。
可以结合代码理解。

代码

#include <bits/stdc++.h> 
using namespace std;
#define ll long long
const int maxn = 200010;
const int mod = 1e9 + 7;

int n; 
int a[maxn], mp[maxn];

void solve() {
	scanf("%d", &n);
	for (int i = 0; i < n; ++i) {
		scanf("%d", &a[i]);
		mp[a[i]] = i;
	}
	ll res = 1;
	int l = mp[0], r = mp[0];
	for (int i = 1; i < n; ++i) {
		if (mp[i] > l && mp[i] < r) {
			res = res * ((r-l+1) - (i));
			res %= mod;
		} else if (mp[i] < l) {
			l = mp[i];
		} else if (mp[i] > r) {
			r = mp[i];
		}
	}
	printf("%lld\n", res);
}

int main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		solve();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值