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;
}