CF888D Almost Identity Permutations

洛谷题面

题目大意

给出 \(n\)\(k\) 计算满足至少有 \(n-k\) 个位置的值 \(a_i=i\)\(1\cdots n\) 的全排列的个数。

题目分析

反过来想,\(n-k\) 个位置 \(a_i=i\),也就是 \(k\) 个位置 \(a_i\neq i\)即错排问题。

详见 \(\texttt{P1595 信封问题}\)

\(k\) 个数的错排个数,有递推公式:

\[d_i=(i-1)\times(d_{i-1}+d_{i-2}) \]

\(d_i\) 表示第 \(i\) 个数的错排个数。其中 \(d_1=0\)\(d_2=1\)

所以我们要在 \(n\) 个位置中选择 \(k\) 个位置放置数,所以答案为 \(C_{n}^{i}\times d_i +1\),其中 \(1\le i\le k\)

注意最后要加 \(1\),因为我们没有计算 \(k=1\) 时的情况。

注意:

  • 记得开 \(\rm long~long\)

代码

#define int long long

const int ma=1005;

int dp[ma];

int C[ma][ma];

int n,k;

inline void init()
{
	C[0][0]=1;
	
	for(register int i=1;i<=1e3;i++)
	{
		C[i][0]=1;
		 
		for(register int j=1;j<=i;j++)
		{
			C[i][j]=C[i-1][j]+C[i-1][j-1];
		}
	}
}

#undef int

int main(void)
{
	#define int long long
	
	n=read(),k=read();
	
	init();
	
	if(k==1)
	{
		printf("1\n");
	}
	
	else
	{
		int ans(0);
		
		dp[2]=1;
		
		for(register int i=3;i<=n;i++)
		{
			dp[i]=(i-1)*(dp[i-1]+dp[i-2]);
		}
		
		for(register int i=2;i<=k;i++)
		{
			ans+=C[n][i]*dp[i];
		}
		
		printf("%lld\n",ans+1);
	}
	
	return 0;
}
posted @ 2021-11-15 22:01  Coros_Trusds  阅读(38)  评论(0)    收藏  举报