组合数中的优化问题
题目来源:P2822 [NOIP2016 提高组] 组合数问题
题目描述很简单,只要上过高中的话还是很好看懂的。
再来看一下数据量
n,m的最大值是2000,代表可以提前打好表然后进行操作
第一个想法就是直接用组合数的公式
暴力计算,看了看貌似可以拿40%左右的数据,没有什么思考难度
但是我们既然都考虑打表了,为什么不用递推的公式捏
这个公式也很好证明,就是直接用了动态规划的思想,选和不选两个状态的叠加
使用这个公式之后再进行取模操作就可以过了吧(假)
void fun(int mod){
record[0][0]=record[1][0]=record[1][1]=1;
for(int i=2;i<maxn;++i)
{
record[i][0]=1;
for(int j=1;j<=i;++j)
{
record[i][j]=record[i-1][j-1]+record[i-1][j];
record[i][j]%=mod;
}
}
}
结果这题数据还挺ex,还给了俩TLE,说明算法还可以优化,思来想去也不会,于是直接去看答案了,原来是用前缀和压缩维度。。。。
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];
ans[i][j]表示选i和j时的答案,上面的前缀和公式参考二维面积前缀和
最后给出ac代码
#include<bits/stdc++.h>
using namespace std;
int const maxn=2001;
int record[maxn][maxn];
long long ans[maxn][maxn];
void fun(int mod){
record[0][0]=record[1][0]=record[1][1]=1;
for(int i=2;i<maxn;++i)
{
record[i][0]=1;
for(int j=1;j<=i;++j)
{
record[i][j]=record[i-1][j-1]+record[i-1][j];
record[i][j]%=mod;
ans[i][j]=ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];
if(!record[i][j]) ans[i][j]++;
}
ans[i][i+1]=ans[i][i];
}
}
int main(void){
int t,k;cin>>t>>k;
fun(k);
for(int i=1;i<=t;++i){
int x,y;
cin>>x>>y;
if(y>x) y=x;
cout<<ans[x][y]<<endl;
}
return 0;
}