神秘题目
Description
在一个平面直角坐标系中,你站在原点,即(0,0)上。你每次可以选择往上下左右中的一个方向走1个单位的距离(不能不走)。问你用k步走到(a,b)的方案数。(只需要k步后在终点即可,不关注途中是否经过终点)
Input
本题有多组数据。
第一行一个整数t,表示数据组数。
接下来t行,第i+1行包含三个整数ai,bi,ki,表示第i次询问终点为(ai,bi),步数为ki。
Output
共t行,每行一个整数,表示答案。
Sample Input
3
1 1 1
0 1 2
4 4 10
Sample Output
0
0
2520
HINT
对于100%的数据,1<t≤106,0≤ai,bi≤106,0≤ki≤3*106。
Solution
(本题解仅适用于我这种只会暴力推导的蒟蒻选手,请开题就旋转45°秒题的大佬们出门左转慢走不送。)
显然,当a+b<k或k-a-b为奇数时,没有符合要求的方案。
从(0,0)到(a,b)至少需要a+b步。
对于剩下的k-a-b步,需满足向左(上)走和向右(下)走的步数相等。
令n=(k-a-b)/2,我们可以得到
$ans=\sum\limits_{i=0}^{n}({{k}\atop{a+2i}})({{a+2i}\atop{i}})({{b+2n-2i}\atop{n-i}})$
$=\sum\limits_{i=0}^{n}\frac{k!}{i!(a+i)!(n-i)!(b+n-i)!}$(注:k-a-2i=a+b+2n-a-2i=b+2n-2i)
接下来是最重要的一步,我们可以发现i+(n-i)=n,(a+i)+(b+n-i)=k-n,n+(k-n)=k,于是我们可以考虑搞一点事情↓
$k!=\frac{k!*n!*(n-k)!}{n!*(n-k)!}$
代入原式可得
$ans=({{k}\atop{n}})\sum\limits_{i=0}^{n}({{n}\atop{i}})({{k-n}\atop{a+i}})$
似乎看不出什么,我们将$({{k-n}\atop{a+i}})$变为$({{k-n}\atop{k-n-a-i}})$。
理性分析一下我们可以发现,$\sum\limits_{i=0}^{n}({{n}\atop{i}})({{k-n}\atop{k-n-a-i}})$表示的是从k个球中取k-n-a个球的方案数,则$ans=({{k}\atop{n}})({{k}\atop{k-n-a}})$,单次询问的时间复杂度O(1),总时间复杂度为O(t)。
Code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int mod=998244353; const int len=25000000; char s[len];int pos=0; inline int rd(){ char c=s[pos++];int flag=1,x=0; for(;c<'0'||c>'9';c=s[pos++])if(c=='-')flag=-1; for(;c>='0'&&c<='9';c=s[pos++])x=x*10+c-'0'; return x*flag; } int fac[3000010]={1},inv[3000010]={1}; int power(int x,int k){ int res=1,s=x; for(;k;k>>=1){ if(k&1) res=(ll)res*s%mod; s=(ll)s*s%mod; } return res; } void prepare(){ for(int i=1;i<=3000000;i++) fac[i]=(ll)fac[i-1]*i%mod; inv[3000000]=power(fac[3000000],mod-2); for(int i=2999999;i;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod; return; } int C(int n,int m){ return (ll)fac[n]*inv[m]%mod*inv[n-m]%mod; } int solve(int a,int b,int k){ int n=(k-a-b)>>1; return (ll)C(k,n)*C(k,k-n-a)%mod; } int main(){ freopen("data_A.in","r",stdin); freopen("ans_A.out","w",stdout); fread(s,1,len,stdin); prepare(); for(int t=rd();t--;){ int a=rd(),b=rd(),k=rd(); if(a+b>k||((k-a-b)&1)){ puts("0"); continue; } printf("%d\n",solve(a,b,k)); } return 0; }