[luogu2822][组合数问题]

题目链接

题解:

对于上面和下面的式子进行分解质因数,然后看看上面的质因数个数减去下面的质因数个数能不能达到k的质因数的要求即可。
分解质因数的时候用对于阶乘分解质因数的常用方法:比如要求1999!中能分解出多少个5,那么就把1999不断的除以5,并且把得到的数相加即可。原因显然。
但是上面方法的复杂度是nnt,明显tle,考虑优化。发现当k固定之后,对于每个n和m是固定的,并且似乎是可以转移的。所以考虑预处理。
用c[i][j]表示\(C_i^j\)是不是符合要求。用g[i][j]表示当m为j,n从j到max满足条件的数量。f[i][j]表示n为1到i,m为1到j时满足条件的数量。
然后只要考虑出f[i][j]的转移即可,显然f[i][j]=f[i][j-1]+g[i][j]
然后只要O(1)查询即可,懒得现将询问读入再预处理,所以前面的预处理全都是到2000的。

代码:

#include<cstdio>
#include<iostream>
using namespace std;
const int N=2000+10;
int t,n,m,k,pre[N][10];
int sushu[10]={0,2,3,5,7,11,13,17,19};
int ksushu[10],kjs[10];
void fenk()
{
	for(int i=1;i<=8;++i)
	{
		if(!k) break;
		while(k%sushu[i]==0)
		{
			kjs[i]++;
			k/=sushu[i];
		}
	}

}
inline void work2(int x)
{
	for(int i=1;i<=8;++i)
	{
		int xx=x;
		while(xx)
		{
			xx/=sushu[i];
			pre[x][i]+=xx;
		}
	}
	return;

}
int g[N][N],f[N][N],c[N][N];
inline int pd(int x,int y)
{
    for(int i=1;i<=8;++i)
       	if(pre[x][i]-pre[y][i]-pre[x-y][i]<kjs[i]) return 0;
    return 1;
}
inline int ps(int x)
{
	if(x==2||x==3) return 1;
	if(x%6!=5&&x%6!=1) return 0;
	for(int i=5;i*i<=x;i+=6)
		if(x%(i+2)==0||x%i==0) return 0;
	return 1;
}
int main()
{
    scanf("%d%d",&t,&k);
    	fenk();
    for(int i=2;i<=2000;++i)
    	work2(i);
    for(int i=1;i<=2000;++i)
        for(int j=1;j<=i;++j)
            if(pd(i,j)) 
            	c[i][j]=1;
    for(int j=1;j<=2000;++j)
        for(int i=j;i<=2000;++i)
            g[i][j]=g[i-1][j]+c[i][j];
    for(int i=1;i<=2000;++i)
        for(int j=1;j<=i;++j)
            f[i][j]=f[i][j-1]+g[i][j];
    int x,y;
    while(t--)
    {

        scanf("%d%d",&x,&y);
        printf("%d\n",f[x][min(x,y)]);
    }
    return 0;
}
posted @ 2018-08-25 19:29  wxyww  阅读(152)  评论(0编辑  收藏  举报