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;
}