HDU 3398 String(数论)

201 篇文章 10 订阅

Description
给出两个整数m和n,要求用n个1和m个0构造一种串,这个串满足任意前缀1的数量不少于0的数量,问这种串有多少个
Input
第一行为一整数T表示用例组数,每组用例为两个正整数n和m
(T<=100, 1 <= m <= n <= 1000000 )
Output
对于每组用例,输出满足条件的串的个数,结果模20100501
Sample Input
1
2 2
Sample Output
2
Solution
将构造串的过程看作一个点从(0,0)到(n,m)的路径,1表示向右走,0表示向上走,方法数为C(n+m,n),任意前缀1数量不少于0即为这条路径不能与直线y=x+1相交,考虑与y=x+1相交的路径,由(0,0)关于y=x+1的对称点为(-1,1)及所有从(-1,1)到(n,m)的路径都与直线y=x+1相交知,所有从(0,0)到(n,m)且与直线y=x+1相交的路径都与从(-1,1)到(n,m)的路径一一对应,故不满足条件的方法数为C(n+m,n+1),故答案为ans=C(n+m,n)-C(n+m,n+1)=(n+m)!(n+1-m)/((n+1)!m!),因为n和m非常大,所以用质因子的加减代替阶乘的乘除,此处用到n!的质因子分解方法,对一个素数p,我们想知道其在n!的质因子分解表达式中的幂指数a,显然n!中含p的项有n/p项,含p^2的项有n/p/p,…,以此类推累加各项即得到a的值。
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm> 
using namespace std;
typedef long long ll;
#define mod 20100501ll
#define maxm 2000011
#define maxn 155555
int p[maxn];
int prime[maxn],res,num;
bool is_prime[maxm];
void get_prime()
{
    prime[0]=2;
    res=1;
    for(int i=3;i<maxm;i+=2)
        if(!is_prime[i])
        {
            for(int j=i;j<maxm;j+=i)
                is_prime[j]=1;
            prime[res++]=i;
        }
}
ll mod_pow(ll a,ll b,ll p)
{
    ll ans=1ll;
    a%=p;
    while(b)
    {
        if(b&1) ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    } 
    return ans;
}
void divide(int n)//对n质因子分解 
{
    int cnt=0;
    while(n>1)
    {
        while(n%prime[cnt]==0)
        {
            p[cnt]++;
            n/=prime[cnt];
        }
        cnt++;
    }
    num=max(num,cnt);
}
void Divide(int n,bool state)//对n!质因子分解 
{
    int cnt=0,temp;
    while(prime[cnt]<=n)
    {
        temp=n;
        while(temp)
        {
            if(state) p[cnt]+=temp/prime[cnt];
            else p[cnt]-=temp/prime[cnt];
            temp/=prime[cnt];
        }
        cnt++;
    }
    num=max(num,cnt);
}
ll get_ans()
{
    ll ans=1ll;
    for(int i=0;i<=num;i++)
        if(p[i])
            ans=mod_pow(1ll*prime[i],1ll*p[i],mod)*ans%mod;
    return ans;
}
int main()
{
    get_prime();
    int t,n,m;
    scanf("%d",&t);
    while(t--)
    {
        num=0;
        memset(p,0,sizeof(p));
        scanf("%d%d",&n,&m);
        divide(n+1-m);
        Divide(n+m,1);
        Divide(n+1,0);
        Divide(m,0);
        ll ans=get_ans();
        printf("%lld\n",ans);
    }
    return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值