题解 P2822 【组合数问题】
Salamander · · 题解
分解质因数最好懂。
先把k分解质因数,然后计算C(i,j)里有多少个k的某个质因数,如果个数比k里有的个数小就证明不可能是k的倍数。
计算n!里有多少个质因数p的方法:nump=n/p+n/p^2+n/p^3+n/p^4+...+n/p^k。这里的/是整除,当p^k>n时终止。
用f[i][j]表示C(i,1-j)里有多少个能整除k,g[i][j]表示C(1-i,1-j)里有多少个能整除k。(前缀和思想)
然后用离线的方式先处理好递推,再统一输出结果。
这题很阴险的卡常,O(n*m)的算法不用读入优化会有几个点TLE。
其他详见代码。
#include<cstdio>
#include<algorithm>
using namespace std;
int read()
{
int in=0,k=1;char c=getchar();
for(;c>'9'||c<'0';c=getchar()) if(c=='-') k=-1;
for(;c<='9'&&c>='0';c=getchar())
in=in*10+c-'0';
return k*in;
}
const int p[]={0,2,3,5,7,11,13,17,19};
int T,k,n,m,maxn=0,maxm=0,que[20001][2],prime[21],num[21];
int f[2001][2001],g[2001][2001];
void fenjie(int t)//把t分解质因数,prime记录质因数,num记录该质因数的个数。
{
int i=1;
while(t>1)
{
if(t%p[i]==0)
{
if(prime[prime[0]]==p[i]){num[prime[0]]++;t/=p[i];}
else {prime[++prime[0]]=p[i],t/=p[i];num[prime[0]]=1;}
}
else i++;
}
}
int calc(int t,int k)//计算t!里有多少个质因数k
{
int ans=0,rec=k;
while(t>=rec)
{
ans+=t/rec;
rec*=k;
}
return ans;
}
bool pd(int i,int j)
{
int a,b,c;
for(int t=1;t<=prime[0];t++)
{
a=calc(i,prime[t]);//根据组合数计算公式,计算n!,m!和(n-m)!里有多少个质因数prime[i]
b=calc(j,prime[t]);
c=calc(i-j,prime[t]);
if(a-b-c<num[t])return 0;//如果个数不够那么不能整除。
}
return 1;
}
int main()
{
T=read();k=read();
fenjie(k);
for(int t=1;t<=T;t++)
{
n=read();m=read();
que[t][0]=n;
que[t][1]=m;
if(n>maxn)maxn=n;
if(m>maxm)maxm=m;
}
for(int i=1;i<=maxn;i++)
for(int j=1;j<=i&&j<=maxm;j++)
{
f[i][j]=f[i][j-1]+pd(i,j);
g[i][j]=g[i-1][min(i-1,j)]+f[i][j];
}//前缀和递推,注意j>i-1的情况
for(int t=1;t<=T;t++)
{
int a=que[t][0],b=que[t][1];
printf("%d\n",g[a][min(a,b)]);//注意b>a的情况。
}
return 0;
}