「CF285E Positions in Permutations」题解

Description

CF285E Positions in Permutations
题意:给定 \(n\)\(k\),求出满足 \(\sum_{i=1}^n[|p_i-i|=1]\)\(n\) 的排列 \(p\) 的数量。

Solution

前置芝士:二项式反演,线性 \(dp\)
恰好为 \(k\) 不好做。
仍然用二项式反演常用思路。把恰好转换为至少。
\(f(k)\) 表示至少有 \(k\) 个位置满足条件的排列数,\(g(k)\) 表示恰好有 \(k\) 个位置满足条件的排列数。有

\[f(k)=\sum_{i=k}^n\binom{i}{k}g(i) \]

由二项式反演有

\[g(k)=\sum_{i=k}^n(-1)^{i-k}\binom{i}{k}f(i) \]

题目要求的就是 \(g(k)\)。现在问题就被转化为如何求 \(\sum_{i=k}^nf(i)\)
简单推理可以得知除开 \(p_1\)\(p_n\) 外,每个位置有两个数字可以选择,且相邻的奇(偶)位置可选择有1个相同。
例如 \(p={1,2,3,4,5,6,7}\),那么可选数字为 \({2,(1,3),(2,4),(3,5),(4,6),(5,7),6}\)
可以发现奇位置和偶位置互不影响,那么我们可以分开考虑再组合起来就可以了。考虑 \(dp\) 转移。
\(dp_{i,j,0/1/2}\) 表示前 \(i\) 个位置中,有 \(j\) 个位置满足要求,第 \(i\) 个位置的选择情况(0为不选,\(p_1,p_n\) 特殊处理)。
易得转移方程
\(dp_{i,j,k}=\begin{cases}dp_{i-2,j,0}+dp_{i-2,j,1}+dp_{i-2,j,2}&k=0\\dp_{i-2,j-1,0}+dp_{i-2,j-1,1}&k=1\\dp_{i-2,j-1,0}+dp_{i-2,j-1,1}+dp_{i-2,j-1,2}&k=2\end{cases}\)
\(n=1/2\) 时直接处理即可。
Code:

#include<cstdio>
#include<iostream>
using namespace std;
const int Mod=1e9+7;
const int MAXN=1e3+5;
const int N=1e3;
#define ll long long
int n,k;
ll fra[MAXN],ofra[MAXN],f[MAXN],dp[MAXN][MAXN][3],Ans,o[2];//奇偶互不影响 
ll ksm(ll a,int b){
	ll ans=1;
	while(b){
		if(b&1) ans=ans*a%Mod;
		a=a*a%Mod;b>>=1;
	}
	return ans;
}
ll C(int n,int m){
	if(n<m) return 0;
	return ofra[m]*(fra[n]*ofra[n-m]%Mod)%Mod;
}
ll Max(ll a,ll b){
	return a>b?a:b;
}
int main(){
	fra[0]=1;o[1]=-1;o[0]=1;
	for(int i=1;i<=N;i++){
		fra[i]=fra[i-1]*i%Mod;
	}
	ofra[N]=ksm(fra[N],Mod-2);
	for(int i=N;i;i--){
		ofra[i-1]=ofra[i]*i%Mod;
	}
	scanf("%d%d",&n,&k);
	if(n==1)
	{
		if(k==0) printf("1");
		else printf("0");
		return 0;
	}
	if(n==2){
		if(k==1) printf("0");
		else printf("1");
		return 0;
	}
	dp[1][0][0]=1;dp[1][1][2]=1;
	dp[2][0][0]=1;dp[2][1][1]=1;dp[2][1][2]=1;
	for(int i=3;i<=n;i++){
		dp[i][0][0]=1;
		for(int j=1;j<=(i+1)>>1;j++){
			for(int k=0;k<3;k++){
				if(k==0){
					dp[i][j][k]=(dp[i-2][j][0]+dp[i-2][j][1]%Mod)+dp[i-2][j][2]%Mod;
					continue;
				}
				if(k==1){
					dp[i][j][k]=(dp[i-2][j-1][1]+dp[i-2][j-1][0])%Mod;
					continue;
				}
				if(i==n){
					continue;
				}
				else dp[i][j][k]=(dp[i-2][j-1][2]+dp[i-2][j-1][1]+dp[i-2][j-1][0])%Mod;
			}
		}
	}
	for(int i=n-1;i<=n;i++){
		for(int j=0;j<=(i+1)>>1;j++){
			dp[i][j][2]=(dp[i][j][0]+dp[i][j][1]+dp[i][j][2])%Mod;
		}
	}
	for(int i=k;i<=n;i++){
		ll tmp=0;
		for(int j=0;j<=i;j++){
			tmp=(tmp+dp[n][j][2]*dp[n-1][i-j][2])%Mod;
		}
		f[i]=tmp*fra[n-i]%Mod;
		Ans=(Ans+((o[(i-k)%2]*C(i,k)%Mod)*f[i]%Mod))%Mod;
		Ans=(Ans+Mod)%Mod;
	}
	printf("%lld\n",Ans);
	return 0;
}

\(\Bbb{End.}\)

\(\Bbb{Thanks}\) \(\Bbb{For}\) \(\Bbb{Reading.}\)

posted @ 2021-12-15 21:48  StranGePants  阅读(50)  评论(0编辑  收藏  举报