组合数问题 题解

组合数问题 题解

题目描述

问题和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;
}
posted @ 2020-11-21 11:56  Vanilla_chan  阅读(235)  评论(0)    收藏  举报