[HNOI2002]奶牛的运算

题目链接

Solution

陈年老题了,但真是一道组合数好题。

根据数学知识,加括号就相当于改变里面的符号,所以我们可以将其看为对符号的修改,问题就变为:一个长度为 \(n-1\) 的符号序列,每次可以选一个区间将其符号反转,问 \(k\) 次操作后,能得到多少不同的序列。

要注意第一个符号没有办法改变,这在最后会提到。

引理

\(k\) 次对任意区间的操作可以看作不超过 \(k\) 次对互不相交且不相邻的区间的操作。

只考虑 \(2\) 个区间的情况即可推广,分情况讨论:

  1. 当区间不交时且不相邻时,可以通过相同的操作次数来实现相同的效果。
  2. 当区间有相交部分,例如 \([1,5]\)\([3,7]\),容易发现对于相交的区间 \([3,5]\) 被改了 \(2\) 次又被改回来了,就相当于只改变了 \([1,2]\)\([6,7]\)。可以通过相同的操作次数来实现相同的效果,注意这里包含了一个区间是另一个区间子集的情况。
  3. 当区间相邻,如 \([1,3]\)\([4,5]\),我们可以通过一次操作 \([1,5]\) 即可实现相同效果。

答案

这样就很清楚了,答案就是选择至多 \(k\) 个不交也不相邻区间的方案数,插板法即可。

注意第一个符号是不能改的,于是头不能插板,而尾插板有意义,所以有 \(n-1\) 个位置,插至多 \(2\times k\) 个偶数板,组合数即可,答案为:

\[\sum_{i=0}^{2k} \binom{n-1}{i} \]

注意增量 \(\Delta=2\)

Code

答案会很大,用高精,这里为了不占版面就不加了。

#include<bits/stdc++.h>
using namespace std;
void read(int &x)
{
	char ch=getchar();
	int r=0,w=1;
	while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
	while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
	x=r*w;
}
int C(int n,int m)
{
	if(m==0)return 1;
	if(n<m)return 0;
	int ans=1;
	for(int i=1;i<=m;i++)
		ans*=(n-i+1),ans/=i;
	return ans;
}
int main()
{
	int n,k;
	read(n);read(k);
	int ans=0;
	for(int i=0;i<=2*k;i+=2)
		ans+=C(n-1,i);
	cout<<ans;
	return 0;
}
posted @ 2022-08-22 16:12  Epoch_L  阅读(41)  评论(0编辑  收藏  举报