「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\) 个位置满足条件的排列数。有
由二项式反演有
题目要求的就是 \(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.}\)
本文来自博客园,作者:{StranGePants},转载请注明原文链接:https://www.cnblogs.com/StranGePants/p/15695447.html