这是一道无比涨姿势的题目
首先总结一下这种输入几个数的题目,
一般不是递推就是数学题
显然,这道题用递推是无法做到O(n)的复杂度的
那我们就考虑这是一道数学题了
我已开始纠结在正向思维了,正向求好像确实不容易;
某牛的报告点醒了我,我们设符合条件的序列为x,不符合的为y
则x+y=c(n+m,n);
现在我们只要求出y即可
然后弱渣的我又卡住了,
还是大牛的报告引用:我们不妨将0看做-1,那么对于一个不合法的序列,必然存在一个位置使得前缀和为-1,我们设这个最小的位置为k,即:a1+a2+……ak=-1,那么前缀和k-1为0,且ak=-1。接着,若我们将所有n+m个的这前k个数字取反,那么得到一个新的数列含有n+1个1和 m-1个-1,这个新的数列有C(n+m,n+1)种。不合法序列与新构造的这个序列是一一对应的关系。
太神了。怎么想到的orz
所以,ans=c(n+m,n)-c(n+m,n+1);
因为n>m, 所以c(m+n,n)-c(n+m,n+1);
(其实现在来看,这就是一个经典的卡特兰数的模型的变形)
由于是组合还需要取模,就要涉及到除法取模;
能回避除法取模的递推法(杨辉三角)复杂度肯定会TLE,
然后我又涨姿势了,
a/b ≡ac (mod p);
bc ≡1 (mod p) 我们把c叫做b的乘法逆元;
一个数a除以另一个数b同余于a乘以b的乘法逆元模p
怎么证呢?
我们把乘法逆元的式子变换一下得 bc=pk+1 k∈Z
则b=(pk+1)/c
则a/b=a*c*(pk+1)≡ac (mod p)
乘法逆元存在的充要条件是gcd(b,p)=1
由于p=20100403>n+m,且是一个质数
显然gcd(b,p)=1;
那么怎么求乘法逆元呢?
看到之前的变换式我们也不难想到,扩展欧几里得
这道题唯一让我欣慰的地方就是,扩展欧几里得写对了……
1 const mo=20100403; 2 var ans:int64; 3 n,m,i:longint; 4 5 procedure exgcd(a,b:int64;var x,y:int64); 6 var xx,yy:int64; 7 begin 8 if b=0 then 9 begin 10 x:=1; 11 y:=0; 12 end 13 else begin 14 exgcd(b,a mod b,x,y); 15 xx:=x; 16 yy:=y; 17 x:=yy; 18 y:=xx-a div b*yy; 19 end; 20 end; 21 22 function re(a,p:int64):int64; 23 var x,y:int64; 24 begin 25 exgcd(a,p,x,y); 26 x:=(x+mo) mod mo; 27 exit(x); 28 end; 29 30 function get(x:int64):int64; 31 var i:int64; 32 begin 33 i:=1; 34 get:=1; 35 while i<x do 36 begin 37 inc(i); 38 get:=get*i mod mo; 39 end; 40 end; 41 42 function c(x,y:int64):int64; 43 var a,b,d:int64; 44 begin 45 a:=get(x); 46 b:=get(y); 47 d:=get(x-y); 48 c:=a*re(b,mo) mod mo*re(d,mo) mod mo; 49 end; 50 51 begin 52 readln(n,m); 53 ans:=c(n+m,n)-c(n+m,n+1); 54 ans:=(ans+mo) mod mo; 55 writeln(ans); 56 end.