组合数问题 题解
组合数问题 题解
题目描述
问题和P2822 组合数问题 - 洛谷基本一样,除了这道题对组合数重定义了。
这道题中组合数\(S(n,m)\)表示将\(n\)个不同的元素拆分成\(m\)个非空集合的方案数。
\(S(0,0)=1,\forall i\ge 1,S(i,0)=0\)。
\(1\le n,\le 2000,2\le k\le 21,1\le t\le 10000\)。
Solution
可以就按照洛谷 P2822 组合数问题类似的思路走。
一眼看出这就是第二类斯特林数。
首先写出递推公式
\[S(n,m)=S(n-1,m-1)+m\times S(n-1,m)
\]
因为只要知道是否能被k整除,所以只要对k取模就好。
预处理\(S(2000,2000)\)之后,再预处理出二维前缀和即可。
时间复杂度\(O(n^2)\)
Code
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d\n",__LINE__);
#else
#define debug
#endif
using namespace std;
template<class T>inline void read(T&x)
{
char ch=getchar();
while(!isdigit(ch))ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
}
inline int read()
{
int x=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
int G[55];
template<class T>inline void write(T x)
{
int g=0;
if(x<0) x=-x,putchar('-');
do{G[++g]=x%10;x/=10;}while(x);
for(re int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int t,k;
LL C[2010][2010],s[2010][2010];
bool book[2010][2010];
int f(int n, int m) {
// debug cout<<n<<" "<<m<<endl;
if(m==1||n==m) return 1;
if(n<m) return 0;
if(book[n][m]) return C[n][m];
book[n][m]=1;
return C[n][m]=(f(n-1,m-1)%k+m*f(n-1,m)%k)%k;
}
void pre()
{
//C[i][j]=C[i-1][j-1]+j*C[i-1][j]
/*
C[0][0]=0;
C[1][0]=0;
C[1][1]=1;
for(int i=2;i<=10;i++)
{
C[i][1]=1;
// cout<<C[i][1]<<" ";
for(int j=1;j<=10;j++)
C[i][j]=C[i-1][j-1]+j*C[i-1][j],C[i][j]%=k,cout<<C[i][j]<<" ";
cout<<endl;
}
*/
for(re int i=1;i<=2000;i++) C[i][1]=C[i][i]=1;
for(re int i=1;i<=2000;i++)
{
for(re int j=1;j<=2000;j++)
{
C[i][j]%=k;
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
if(!C[i][j]) s[i][j]++;
}
s[i][i+1]=s[i][i];
}
}
int main()
{
// freopen("problem.in","r",stdin);
// freopen("problem.out","w",stdout);
t=read();
k=read();
for(re int i=2;i<=2001;i++)
for(re int j=1;j<=2001;j++)
f(i,j);
pre();
re int i,j;
while(t--)
{
i=read();
j=read();
cout<<s[i][min(j,i)]+i<<endl;
}
return 0;
}

浙公网安备 33010602011771号