题目
思路
化简式子题……因为一眼看出
a n s = ∑ j = 0 b ∑ i = j + 1 a ( a i ) ( b j ) ans=\sum_{j=0}^{b}\sum_{i=j+1}^{a}{a\choose i}{b\choose j} ans=j=0∑bi=j+1∑a(ia)(jb)
因为 ( a i ) = ( a a − i ) {a\choose i}={a\choose a-i} (ia)=(a−ia) 且 ( b j ) = ( b b − j ) {b\choose j}={b\choose b-j} (jb)=(b−jb) ,所以上式的两倍等于
∑ j = 0 b ∑ i = j + 1 a ( a i ) ( b j ) + ∑ j = 0 b ∑ i = j + 1 a ( a a − i ) ( b b − j ) \sum_{j=0}^{b}\sum_{i=j+1}^{a}{a\choose i}{b\choose j}+\sum_{j=0}^{b}\sum_{i=j+1}^{a}{a\choose a-i}{b\choose b-j} j=0∑bi=j+1∑a(ia)(jb)+j=0∑bi=j+1∑a(a−ia)(b−jb)
不妨把第二个求和符号分成两个,一个部分计算 a − i ≤ b − j a-i\le b-j a−i≤b−j 的情形,另一个则计算 a − i > b − j a-i>b-j a−i>b−j 的情形。那么答案的两倍为
∑ j = 0 b ∑ i = j + 1 a ( a i ) ( b j ) + ∑ j = 0 b ∑ i = j + 1 a − b + j − 1 ( a a − i ) ( b b − j ) + ∑ j = 0 b ∑ i = a − b + j a ( a a − i ) ( b b − j ) \sum_{j=0}^{b}\sum_{i=j+1}^{a}{a\choose i}{b\choose j}+\sum_{j=0}^{b}\sum_{i=j+1}^{a-b+j-1}{a\choose a-i}{b\choose b-j}+\sum_{j=0}^{b}\sum_{i=a-b+j}^{a}{a\choose a-i}{b\choose b-j} j=0∑bi=j+1∑a(ia)(jb)+j=0∑bi=j+1∑a−b+j−1(a−ia)(b−jb)+j=0∑bi=a−b+j∑a(a−ia)(b−jb)
将最右边一项单独拿出来看看。显然 a − i ∈ [ 0 , b − j ] a-i\in[0,b-j] a−i∈[0,b−j] ,所以如果对 b − j b-j b−j 换元,这一项就是 ∑ j = 0 b ∑ i = 0 j ( a i ) ( b j ) \sum_{j=0}^{b}\sum_{i=0}^{j}{a\choose i}{b\choose j} ∑j=0b∑i=0j(ia)(jb) 。和第一项非常相似!这两项合在一起得到
∑ j = 0 b ( b j ) ∑ i = 0 a ( a i ) = 2 a + b \sum_{j=0}^{b}{b\choose j}\sum_{i=0}^{a}{a\choose i}=2^{a+b} j=0∑b(jb)i=0∑a(ia)=2a+b
于是
a n s = 2 a + b − 1 + 1 2 ∑ j = 0 b ∑ i = j + 1 a − b + j − 1 ( a a − i ) ( b b − j ) ans=2^{a+b-1}+\frac{1}{2}\sum_{j=0}^{b}\sum_{i=j+1}^{a-b+j-1}{a\choose a-i}{b\choose b-j} ans=2a+b−1+21j=0∑bi=j+1∑a−b+j−1(a−ia)(b−jb)
头疼的肯定是后面这一坨。单独考虑它。目前长得有点难看。 ( a a − i ) = ( a i ) {a\choose a-i}={a\choose i} (a−ia)=(ia) 毫无疑问。然后换个枚举方式,枚举 i − j i-j i−j 的值,得到后面那一坨等于
1 2 ∑ j = 0 b ∑ i = 1 a − b − 1 ( a i + j ) ( b b − j ) \frac{1}{2}\sum_{j=0}^{b}\sum_{i=1}^{a-b-1}{a\choose i+j}{b\choose b-j} 21j=0∑bi=1∑a−b−1(i+ja)(b−jb)
注意到
(
i
+
j
)
+
(
b
−
j
)
=
i
+
b
(i+j)+(b-j)=i+b
(i+j)+(b−j)=i+b ,所以我们可以大胆使用
(
n
+
m
x
)
=
∑
i
=
0
+
∞
(
n
i
)
(
m
x
−
i
)
{n+m\choose x}=\sum_{i=0}^{+\infty}{n\choose i}{m\choose x-i}
(xn+m)=∑i=0+∞(in)(x−im) 这一结论。结果写下这句话的我第二天就不知道为什么这是对的了。
其实就是先枚举 i i i ,于是 i + b i+b i+b 为定值了,而此时 b − j b-j b−j 取遍 [ 0 , b ] [0,b] [0,b] ,于是内层求和就是 ( a + b b + i ) {a+b\choose b+i} (b+ia+b) 惹。于是这一坨就是
1 2 ∑ i = 1 a − b − 1 ( a + b i + b ) = 1 2 ∑ i = b + 1 a − 1 ( a + b i ) \frac{1}{2}\sum_{i=1}^{a-b-1}{a+b\choose i+b}=\frac{1}{2}\sum_{i=b+1}^{a-1}{a+b\choose i} 21i=1∑a−b−1(i+ba+b)=21i=b+1∑a−1(ia+b)
我们要对 1 0 k 10^k 10k 取模,发现 2 2 2 是没有逆元的。怎么直接除掉 2 2 2 呢?
分两种情况。当 a + b a+b a+b 为奇数时,注意到很特殊的一点, [ b + 1 , a − 1 ] [b+1,a-1] [b+1,a−1] 的中点刚好是 a + b 2 \frac{a+b}{2} 2a+b ,所以这是对称的,只求一半就做到了。 a + b a+b a+b 为偶数时,中间恰好剩下一个数。不过你在杨辉三角里一眼就能看出来,它就是上一层的中间那个数。所以也可以做到。
组合数要用扩展 L u c a s \tt Lucas Lucas 来求。要魔改一下。复杂度最终是 O ( T n log a + 5 k k ) \mathcal O(Tn\log a+5^kk) O(Tnloga+5kk) 的,其中 n = a − b ≤ 1 0 5 n=a-b\le 10^5 n=a−b≤105 。
代码
调了很久,终于发现:不能使用递推写法求逆元。因为 x x x 不是 p p p 的倍数时, p k m o d x p^k\bmod x pkmodx 也可能是 p p p 的倍数——比如商是 p p p 的倍数。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline int qkpow(int_ b,int_ q,int Mod){
int ans = 1;
for(; q; q>>=1,b=b*b%Mod)
if(q&1) ans = ans*b%Mod;
return ans;
}
template < int p, int pk >
struct LUCAS{
int jc[pk+1]; // factorial
int invjc[pk+1]; // inv of jc
LUCAS(){
int phi = pk/p*(p-1);
jc[0] = invjc[0] = 1;
jc[1] = invjc[1] = 1;
for(int i=2; i<=pk; ++i){
jc[i] = jc[i-1]; // copy
invjc[i] = invjc[i-1];
if(i%p == 0) continue;
jc[i] = 1ll*jc[i]*i%pk;
invjc[i] = 1ll*invjc[i]
*qkpow(i,phi-1,pk)%pk;
}
}
int Lucas(int_ n,int_ m){
if(n < m || m < 0) return 0;
int z = 0; int_ ans = 1;
for(int_ i=n; i; i/=p){
ans = ans*jc[i%pk]%pk;
z += i/pk; // how many [1,pk]
}
for(int_ i=m; i; i/=p){
ans = ans*invjc[i%pk]%pk;
z -= i/pk; // cashier
}
for(int_ i=n-m; i; i/=p){
ans = ans*invjc[i%pk]%pk;
z -= i/pk; // counter
}
if(z > 0) // something good
ans = ans*qkpow(jc[pk],z,pk)%pk;
else if(z < 0) // can't be worse
ans = ans*qkpow(invjc[pk],-z,pk)%pk;
z = 0; // 现在求 C(n,m) 中 p 的指数
for(int_ i=p; i<=n; i*=p)
z += (n/i)-(m/i)-((n-m)/i);
ans = ans*qkpow(p,z,pk)%pk;
return ans;
}
};
const int MaxN = 1953125; // 5^10
const int inv = 1537323; // 1/512 mod MaxN
LUCAS< 5,MaxN > five;
const int inv512 = 109; // 1/MaxN mod 512
LUCAS< 2,512 > two;
const int Mod = 1000000000; // 1e9
int C(int_ n,int_ m){
int_ first = two.Lucas(n,m);
// printf("f = %lld\n",first);
int_ second = five.Lucas(n,m);
// printf("s = %lld\n",second);
int ans = first*inv512*MaxN%Mod;
ans += second*inv*512%Mod;
return ans%Mod;
}
int op[20]; // output
int main(){
int_ a, b; int k;
while(~scanf("%lld",&a)){
scanf("%lld %d",&b,&k);
int ans = qkpow(2,a+b-1,Mod);
for(int_ i=b+1; i<a+b-i; ++i)
ans = (ans+C(a+b,i))%Mod;
if(a == b) // special case
ans = (ans+Mod-C(a+b-1,b-1))%Mod;
else if(!((a+b)&1)) // even number
ans = (ans+C(a+b-1,(a+b)/2))%Mod;
for(int i=1; i<=9; ++i,ans/=10)
op[i] = ans%10; // get digit
for(int i=k; i>=1; --i)
putchar(op[i]+'0');
putchar('\n');
}
return 0;
}