HDU 3398 String(整数唯一分解定理)

String


Problem Description
Recently, lxhgww received a task : to generate strings contain '0's and '1's only, in which '0' appears exactly m times, '1' appears exactly n times. Also, any prefix string of it must satisfy the situation that the number of 1's can not be smaller than the number of 0's . But he can't calculate the number of satisfied strings. Can you help him?
 

Input
T(T<=100) in the first line is the case number.
Each case contains two numbers n and m( 1 <= m <= n <= 1000000 ).
 

Output
Output the number of satisfied strings % 20100501.
 

Sample Input
  
  
1 2 2
 

Sample Output
2

【思路分析】
   个人感觉这个题目的重点不是整数唯一分解定理,而是推导的过程。网上所有的题解差不多都是转换为二维坐标里面的方法,特别巧妙,值得反复揣测,由于都是别人的思路,在这里就不赘述了。最终推导出的结果是:C(n+m,m) - C (m+n,m-1)
   将上式结果展开,得到:
 (n+1-m)*(n+m)!  /  (m! *(n+1)!) ,n和m的范围为0到1000000,直接递归求解的话肯定会T,因此这里需要利用整数唯一分解定理进一步化简。
   首先可以根据素数筛法找到不超过(n + m)之内的素数并存到数组prime中,然后对于每一个素数prime[i]再去和(n + 1 - m)、
(n+m)!m!(n+1)!这四项相除直到得到最终的次幂cnt,再将prime[i]的cnt次幂这个结果存在ans中,即 ans *= prime[i] ^ cnt(注意取模的情况),循环完prime数组中所有的素数后便可以得到最终的结果。

代码如下:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1000005;
const int mod = 20100501;
bool isPrime[maxn * 2];
int prime[maxn * 2];
int len,n,m;
ll ans;
void getPrime(int n)//筛素数
{
    len = 0;
    memset(isPrime,1,sizeof(isPrime));
    isPrime[0] = isPrime[1] = 0;
    for(int i = 2;i <= n;i++)
    {
        if(isPrime[i])
        {
            prime[len++] = i;
            for(int j = 2 * i;j <= n;j += i)
            {
                isPrime[j] = 0;
            }
        }
    }
}
int getNum(int p,int n)//计算n!内有多少个p相乘
{
    int num = 0;
    while(n)
    {
        n /= p;
        num += n;
    }
    return num;
}
void init()
{
    ans = 1;
    scanf("%d %d",&n,&m);
}
void solve()
{
    int nm = n + 1 - m;
    for(int i = 0;i < len && prime[i] <= (n + m);i++)//素数最大到(n + m)
    {
        int cnt = 0;
        while(nm % prime[i] == 0)//计算nm的素数的幂次
        {
            nm /= prime[i];
            cnt++;
        }
        cnt += getNum(prime[i],n + m) - getNum(prime[i],m) - getNum(prime[i],n + 1);
        //整个式子的prime[i]的幂次
        for(int j = 1;j <= cnt;j++)
        {
            ans *= prime[i];
            if(ans >= mod)
                ans %= mod;
        }
    }
    printf("%I64d\n",ans);
}
int main()
{
    int t;
    scanf("%d",&t);
    getPrime(2 * maxn);
    while(t--)
    {
        init();
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值