HDU 3398 String

String
Time Limit : 4000/2000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 1 Accepted Submission(s) : 1
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


这题好难…只好跟着大神的题解做了
①在直角坐标系中 从(0,0)出发 遍历字符串 如果遇到一个1 就向上走 遇到一个0就向右走 那n个1 m个0组成的字符串 一定会走到(n,m) 一共有(n+m,n)或C(n+m,m)种情况
②显然 对于每一步 都不能使坐标(x,y)出现x < y 即不能走到y=x-1这条直线上
作一条y=x-1的直线 (0,0)关于y=x-1的对称点是(1,-1)
③从(1,-1)出发 显然 要到达(n,m) 路径必然和y=x-1有交点
将这从(1,-1)出发到(n,m)的路径 在y=x-1下方的部分关于y=x-1翻转 就得到了所有从(0,0)出发的到(n,m) 而且不符合情况(相交与y=x-1)的方案
从(1,-1)到(n,m) 要向右走m步 向上n+1步 方案数是C(n+m,n+1)
所以 符合情况的方案数有C(n+m,n)-C(n+m,n+1)种

然而 还没完….因为20100501不是素数 所以并不能求乘法逆元(即使是素数 也要O(n)的复杂度 基本上也是TLE)
所以把n!分解为x1^p1*x2^p2*x3^p3*….*xn^pn xi为n!的质因子
而且n/x就等于 1,2,3…,n中能整除x的值的数量
所以n!中 包含的质因子x数量=n/x+n/x^2+n/x^3+……
先素数筛法求出2e6以内的素数
p[i]记录第i个素数因子在解中的数量
因为解必然为整数 最终必然p[i]>=0 用快速幂求解即可

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<time.h>
#include<math.h>
#include<list>
#include<cstring>
#include<fstream>
#include<bitset>
//#include<memory.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define INF 1000000007

#define MOD 20100501

const int MAX=2*1000000;//注意 要分解n+m以内的素数 所以是2e6
bool prime[MAX+5];//prime[i]=i是否为素数
vector<int>prim;//prim[i]记录第i个素数的值
vector<int>p;//prim[i]在解中的幂次

void iniprim(){
    //素数筛法
    fill(prime+2,prime+MAX+5,true);
    int end=sqrt(MAX+3);
    for(int i=2;i<=end;++i)
        if(prime[i])
            for(int j=i*i;j<=MAX;j+=i)
                prime[j]=false;
    //将所有素数放进prim
    prim.push_back(2);
    for(int i=3;i<=MAX;i+=2)
        if(prime[i])
            prim.push_back(i);
    p.resize(prim.size());
}

void divi_fac(int n,int flag){
//计算n!的素因子幂次 flag表示+或-掉n!的幂次
    for(int i=0;i<prim.size();++i){
        if(prim[i]>n)
            break;
        int temp=n;
        while(temp>0){
            p[i]+=flag*temp/prim[i];
            temp/=prim[i];
        }
    }
}

void divi(int n){//计算n的素因子幂次
    for(int i=0;i<prim.size()&&n>0;++i)
        while(n%prim[i]==0){
            ++p[i];
            n/=prim[i];
        }
}

ll quick_pow(ll x,int n){//快速幂
    ll res=1;
    while(n){
        if(n&1)
            res=(res*x)%MOD;
        x=(x*x)%MOD;
        n>>=1;
    }
    return res;
}

ll cau_res(){//对所有prim[i]^p[i]进行连乘 计算最终结果
    ll res=1;
    for(int i=0;i<p.size();++i)
        if(p[i])
            res=(res*quick_pow(prim[i],p[i]))%MOD;
    return res;
}

int main()
{
    //freopen("/home/lu/文档/r.txt","r",stdin);
    //freopen("/home/lu/文档/w.txt","w",stdout);
    iniprim();
    int t,n,m;
    cin>>t;
    while(t--){
        cin>>n>>m;
        if(n<m){
            cout<<0<<endl;
            continue;
        }
        fill(p.begin(),p.end(),0);
        divi(n-m+1);
        divi_fac(n+m,1);
        divi_fac(n+1,-1);
        divi_fac(m,-1);
        cout<<cau_res()<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值