foj 1753

Problem 1753 Another Easy Problem

Accept: 326    Submit: 1522
Time Limit: 1000 mSec    Memory Limit : 32768 KB

Problem Description

小TT最近学习了高斯消元法解方程组,现在他的问题来了,如果是以下的方程,那么应该如何解呢?

C(n1,m1)==0 (mod M)

C(n2,m2)==0 (mod M)

C(n3,m3)==0 (mod M)

................

C(nk,mk)==0 (mod M)

小TT希望你告诉他满足条件的最大的M

其中C(i,j)表示组合数,例如C(5,2)=10,C(4,2)=6...

Input

输入数据包括多组,每组数据的第一行是一个正整数T(1<=T<=150)表示接下来描述的T个方程

接下来T行,每行包括2个正整数ni,mi (1<=mi<=ni<=100000)

Output

输出一行答案,表示满足方程组的最大M。

Sample Input

3
100 1
50 1
60 1

Sample Output

10

Source

FZU 2009 Summer Training IV--Number Theory
题目大意:求T个组合数的最大公约数。
解题思路:每个数可以表示为素因子的幂的乘积的形式,那么如果一个数比较大不能直接记录,那么就记录它的素因子及每个素因子的个数就可以了,即使这样直接求组合数的素因子及其个数还是有困难,这里用到一个方法,c(n,m)=n!/(m!*(n-m)!),那么c(n,m)的素因子及其个数就等于n!的素因子的个数减去m!的素因子的个数再减去(n-m)!的素因子个数,求n!的素因子p的个数的方法是:
k=n,ans=0;
while(k)
    ans+=k/p,k/=p;
每个素因子小于等于n,这样就可以较方便快捷的将组合数用素因子来表示了。
既然已经将组合数用素因子表示了,那么不断更新缩小素因子个数就能求出最大公约数了。
这里有个优化:素因子只用找到T个n中最小的n值就可以,因为再大的也没用,会被最小的n删掉的。
结果记得用long long!!!!
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 100001
bool isnotp[N];
int p[N],num[N],t[N];
int n[105],m[105];
int cnt=0,T,minn;
void makeplist()
{
    memset(isnotp,0,sizeof(isnotp));
    isnotp[1]=1;
    p[cnt++]=2;
    int i,j;
    for(i=4;i<N;i+=2)
        isnotp[i]=1;
    for(i=3;i<N;i+=2)
    {
        if(!isnotp[i])
            p[cnt++]=i;
        for(j=0;j<cnt&&i*p[j]<N;++j)
        {
            isnotp[i*p[j]]=1;
            if(i%p[j]==0)
                break;
        }
    }
//    cout<<"makeplist end"<<endl;
//    for(i=0;i<cnt&&p[i]<=100;++i)
//        printf("%d ",p[i]);
//    printf("\n");
}
void work()
{
    memset(num,-1,sizeof(num));
    int i,j,k,cas=0;
    long long ans=1;
    for(i=0;i<T;++i)
    {
        cas++;
        memset(t,0,sizeof(t));
        for(j=0;j<cnt&&p[j]<=minn;++j)
        {
            k=n[i];
            while(k>0)
            {
                t[p[j]]+=k/p[j];
                k/=p[j];
            }
        }
//        printf("%d-1 end\n",cas);
//        for(j=1;j<=minn;++j)  
//            if(t[j])
//                cout<<t[j]<<" ";
//        cout<<endl;
        for(j=0;j<cnt&&p[j]<=minn;++j)
        {
            k=m[i];
            while(k)
            {
                t[p[j]]-=k/p[j];
                k/=p[j];
            }
        }
//        printf("%d-2 end\n",cas);
//        for(j=1;j<minn;++j)
//            if(t[j])
//                cout<<t[j]<<" ";
//        cout<<endl;
        for(j=0;j<cnt&&p[j]<=minn;++j)
        {
            k=n[i]-m[i];
            while(k)
            {
                t[p[j]]-=k/p[j];
                k/=p[j];
            }
        }
//        printf("%d-3 end\n",cas);
//        for(j=1;j<minn;++j)
//            if(t[j])
//                cout<<t[j]<<" ";
//        cout<<endl;
        for(j=0;j<cnt&&p[j]<=minn;++j)
        {
            if(num[p[j]]==-1)
                num[p[j]]=t[p[j]];
            else if(t[p[j]]<num[p[j]])
                num[p[j]]=t[p[j]];
        }
//        printf("%d-4 end\n",cas);
    }
    for(j=0;j<cnt&&p[j]<=minn;++j)
    {
        k=num[p[j]];
        while(k>0)
            ans*=p[j],k--;
    }
    printf("%I64d\n",ans);
}
int main()
{
    int i;
    makeplist();
    while(scanf("%d",&T)!=EOF)
    {
        minn=N;
        for(i=0;i<T;++i)
        {
            scanf("%d %d",n+i,m+i);
            if(n[i]<minn)
                minn=n[i];
        }
        work();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值