Solution
陈年老题了,但真是一道组合数好题。
根据数学知识,加括号就相当于改变里面的符号,所以我们可以将其看为对符号的修改,问题就变为:一个长度为 \(n-1\) 的符号序列,每次可以选一个区间将其符号反转,问 \(k\) 次操作后,能得到多少不同的序列。
要注意第一个符号没有办法改变,这在最后会提到。
引理
\(k\) 次对任意区间的操作可以看作不超过 \(k\) 次对互不相交且不相邻的区间的操作。
只考虑 \(2\) 个区间的情况即可推广,分情况讨论:
- 当区间不交时且不相邻时,可以通过相同的操作次数来实现相同的效果。
- 当区间有相交部分,例如 \([1,5]\) 和 \([3,7]\),容易发现对于相交的区间 \([3,5]\) 被改了 \(2\) 次又被改回来了,就相当于只改变了 \([1,2]\) 和 \([6,7]\)。可以通过相同的操作次数来实现相同的效果,注意这里包含了一个区间是另一个区间子集的情况。
- 当区间相邻,如 \([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;
}
标签:ch,运算,符号,插板,相邻,HNOI2002,区间,操作,奶牛
From: https://www.cnblogs.com/LAK666/p/16613099.html