P16791 [蓝桥杯 2026 国 A] 神秘排列 题解

Problem Discription\color{blue}{\texttt{Problem Discription}}Problem Discription

Analysis\color{blue}{\texttt{Analysis}}Analysis

设大小为 xxx 的元素在平衡位置 iii。设在 1,i−11,i-11,i−1 中有 uuu 个数小于 xxx,则有 (i−1−u)(i-1-u)(i−1−u) 个数大于 xxx。一共有 (n−x)(n-x)(n−x) 个数大于 xxx,所以在 i+1,ni+1,ni+1,n 中大于 xxx 的数有 (n−x−(i−1−u))=n−x−i+u+1(n-x-(i-1-u))=n-x-i+u+1(n−x−(i−1−u))=n−x−i+u+1 个。

因为 iii 是平衡位置,所以 u=n−x−i+u+1u=n-x-i+u+1u=n−x−i+u+1,所以 0=n−x−i+10=n-x-i+10=n−x−i+1,即 x=n−i+1x=n-i+1x=n−i+1。

可见,平衡位置上的元素其实是确定了的。

想到一个思维游戏。桌面上有 100100100 个硬币,其中 101010 个正面朝上,其余反面朝上。你现在闭着眼睛看不见,硬币被随机打乱了。你需要把硬币分成两组(不需要一样多),还可以进行任意次的翻转操作(每次操作把一枚硬币翻过来,正面变成反面,反面变成正面)。问如何操作才能保证两堆硬币正面朝上的数量一样多。

正解非常的巧妙。你把任意 101010 枚硬币拿出来成一堆,然后把它们全部翻转,就能完成要求。

证明:假设这 101010 枚硬币中正面朝上的数量是 xxx,则反面朝上 (10−x)(10-x)(10−x),初始 101010 个正面朝上,剩下 909090 枚硬币中正面朝上 (10−x)(10-x)(10−x)。翻转后正面朝上的数量一定相等,均为 (10−x)(10-x)(10−x)。

因此,平衡位置数量是 kkk,只需要选出 kkk 个位置,放上正确的元素,剩下的全错排即可。

答案为:

∑i=⌈n2⌉n(ni)Dn−i\sum\limits_{i=\left \lceil \frac{n}{2} \right \rceil}^{n} \binom{n}{i}D_{n-i}i=⌈2n⌉∑n(in)Dn−i

其中 DiD_{i}Di 表示全错排数。

时间复杂度 O(n)O(n)O(n)。

Code\color{blue}{\text{Code}}Code

cpp 复制代码
int D[N],fac[N],inv[N],n,ans;

int ksm(int a,int b){
	int res=1;
	while (b){
		if (b&1) res=1ll*res*a%mod;
		a=1ll*a*a%mod;b>>=1;
	}
	
	return res;
}

void get_fac_and_inv(int n){
	fac[0]=1;
	for(int i=1;i<=n;i++)
		fac[i]=1ll*i*fac[i-1]%mod;
	
	inv[n]=ksm(fac[n],mod-2);
	for(int i=n;i>=1;i--)
		inv[i-1]=1ll*i*inv[i]%mod;
}

int C(int n,int m){
	return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int main(){
	scanf("%d",&n);
	
	get_fac_and_inv(n);
	
	D[1]=0;D[0]=1;
	for(int i=2;i<=n;i++)
		D[i]=1ll*(i-1)*((D[i-1]+D[i-2])%mod)%mod;
	
	int m=(n>>1);
	if (n&1) m++;
	
	for(int i=m;i<=n;i++)
		ans=(ans+1ll*C(n,i)*D[n-i]%mod)%mod;
	
	printf("%d",ans); 
	
	return 0;
}