-->

Day 2 T1

题目描述

组合数表示的是从n个物品中选出m个物品的方案数。举个例子,从(1,2,3) 三个物品中选择两个物品可以有(1,2),(1,3),(2,3)这三种选择方法。根据组合数的定 义,我们可以给出计算组合数的一般公式:

其中n! = 1 × 2 × · · · × n

小葱想知道如果给定n,m和k,对于所有的0 <= i <= n,0 <= j <= min(i,m)有多少对 (i,j)满足是k的倍数。

输入输出格式

输入格式:

 

第一行有两个整数t,k,其中t代表该测试点总共有多少组测试数据,k的意义见 【问题描述】。

接下来t行每行两个整数n,m,其中n,m的意义见【问题描述】。

 

输出格式:

 

t行,每行一个整数代表答案。

 

输入输出样例

输入样例#1:
1 2
3 3
输出样例#1:
1
输入样例#2:
2 5
4 5
6 7
输出样例#2:
0
7

说明

【样例1说明】

在所有可能的情况中,只有是2的倍数。

【子任务】

//这个方法比较简单 
//由组合数可知,c(m,n)=(n-m+1)!/(m!) ,那么要想组合数能整除k,就必须统计k的质因数 是否包涵与c的质因数。 
// 用g[i]表示i中k的质因数个数 
// 用f[i]表示i!中k的质因数个数 
//因为2-21 中k能分解成两种不同的质因数,所以有g2,f2 
//用 z[i][j]代表 c(1到j,i)中能被k整除的个数 
//用 u[i][j]代表 c(1到j,1到i)中能被k整除的个数

 
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <string.h>
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;
}
int k,k2,nk,nk2,n,m,all,g[2010],g2[2010],jy[2010][2010];

int a[3],f[2010],f2[2010],t,qu[10010][3],maxn=0;

bool check(int n,int m)  //判断组合数  C(m,n) 是否能被k整除 
{
    int a=f[n]-f[n-m+1-1];
    int b=f[m];
    int c=f2[n]-f2[n-m+1-1];
    int d=f2[m];
    if(a-b>=nk&&c-d>=nk2)
    {
        return 1;
    }else
    return 0;
}

int h[2010][2010],u[2010][2010],maxm,z[2010][2010];
int main()
{
//    freopen("problem.in","r",stdin);
//    freopen("problem.out","w",stdout);
    scanf("%d%d",&t,&k);
    
    if(k==4) k=2,k2=0,nk=2,nk2=0;      //打表爆力分解质因数 分成 k,k2 
    else if(k==2) k=2,k2=0,nk=1,nk2=0;
    else if(k==3) k=3,k2=0,nk=1,nk2=0;
    else if(k==5) k=5,k2=0,nk=1,nk2=0;
    else if(k==7) k=7,k2=0,nk=1,nk2=0;
    else if(k==11) k=11,k2=0,nk=1,nk2=0;
    else if(k==13) k=13,k2=0,nk=1,nk2=0;
    else if(k==17) k=17,k2=0,nk=1,nk2=0;
    else if(k==19) k=19,k2=0,nk=1,nk2=0;
    else if(k==6) k=2,k2=3,nk=1,nk2=1;
    else if(k==8) k=2,k2=0,nk=3,nk2=0;
    else if(k==9) k=3,k2=0,nk=2,nk2=0;
    else if(k==10) k=2,k2=5,nk=1,nk2=1;
    else if(k==12) k=2,k2=3,nk=2,nk2=1;
    else if(k==14) k=2,k2=7,nk=1,nk2=1;
    else if(k==15) k=5,k2=3,nk=1,nk2=1;
    else if(k==16) k=2,k2=0,nk=4,nk2=0;
    else if(k==18) k=2,k2=3,nk=1,nk2=2;
    else if(k==20) k=2,k2=5,nk=2,nk2=1;
    else if(k==21) k=7,k2=3,nk=1,nk2=1;
    
    for(int i=1;i<=t;i++)
    {
        qu[i][0]=read();
        qu[i][1]=read();
        qu[i][1]=min(qu[i][1],qu[i][0]);
        if(qu[i][0]<qu[i][1]) qu[i][1]=qu[i][0];
        if(qu[i][0]>maxn) maxn=qu[i][0];
        if(qu[i][1]>maxm) maxm=qu[i][1];
    }
    //计算1到最大n 的每个数中有k 有多少个 
    for(int i=1;i<=maxn;i++)
    {
        int j=0,q=i;
        while(q%k==0) q/=k,j++;
        g[i]=j;
    }
    //计算1到最大n 的每个数中有k2 有多少个
    if(k2!=0)
    for(int i=1;i<=maxn;i++)
    {
        int j=0,q=i;
        while(q%k2==0) q/=k2,j++;
        g2[i]=j;
    }
    memset(jy,0xfffffff,sizeof(jy));
    //计算前缀 即 1到i 中有多少个k 
    for(int i=1;i<=maxn;i++)
        f[i]+=f[i-1]+g[i];
    //1到i  中有多少个k2
    if(k2!=0) for(int i=1;i<=maxn;i++)
                f2[i]+=f2[i-1]+g2[i];
    //计算z与u 
    for(int i=1;i<=maxn;i++)
        for(int j=1;j<=i&&j<=maxm;j++)
            z[i][j]=z[i][j-1]+check(i,j),
            u[i][j]=u[i-1][min(i-1,j)]+z[i][j];
    
    for(int i=1;i<=t;i++)
        printf("%d\n",u[qu[i][0]][qu[i][1]]);
    return 0;
}

 

posted @ 2016-11-26 10:10  yangyaojia  阅读(173)  评论(0编辑  收藏  举报