题目
题意概括: T T T 次询问,每次给出 n , k n,k n,k,求 ∑ i = 0 k C n i % 2333 \sum_{i = 0}^{k} C_{n}^{i} \ \% \ 2333 ∑i=0kCni % 2333。 1 ≤ T ≤ 1 0 5 , 1 ≤ n , k ≤ 1 0 18 1\leq T \leq10^5,1\leq n,k \leq 10^{18} 1≤T≤105,1≤n,k≤1018。
分析:
看到 模数是质数 并且组合数的上下标都很大,可以想到 Lucas 定理。我们根据取模后的到的余数对这 k k k 个位置进行分类,然后计算贡献。
当 k ≥ m o d k \geq mod k≥mod 时:
r e s = ∑ u = 0 m o d − 1 ∑ i = 0 ⌊ k + 1 m o d ⌋ − 1 C n u + i × m o d + g e t ( n , ⌊ k + 1 m o d ⌋ , ⌊ k + 1 m o d ⌋ × m o d , k ) \Large res = \sum_{u=0}^{mod-1}\sum_{i = 0}^{\left \lfloor \frac{k + 1}{mod} \right \rfloor - 1} C_{n}^{u + i\times mod} + get(n,\left \lfloor \frac{k + 1}{mod} \right \rfloor , \left \lfloor \frac{k + 1}{mod} \right \rfloor \times mod, k) res=∑u=0mod−1∑i=0⌊modk+1⌋−1Cnu+i×mod+get(n,⌊modk+1⌋,⌊modk+1⌋×mod,k)
= ∑ u = 0 m o d − 1 C n u × ∑ i = 0 ⌊ k + 1 m o d ⌋ − 1 C n m o d i + g e t ( n , ⌊ k + 1 m o d ⌋ , ⌊ k + 1 m o d ⌋ × m o d , k ) \Large \;\;\;\;\;\;=\sum_{u=0}^{mod-1}C_{n}^{u} \times \sum_{i = 0}^{\left \lfloor \frac{k + 1}{mod} \right \rfloor - 1}C_{\frac{n}{mod}}^{i} + get(n,\left \lfloor \frac{k + 1}{mod} \right \rfloor , \left \lfloor \frac{k + 1}{mod} \right \rfloor \times mod, k) =∑u=0mod−1Cnu×∑i=0⌊modk+1⌋−1Cmodni+get(n,⌊modk+1⌋,⌊modk+1⌋×mod,k)
其中 g e t ( n , c n t , s t , e d ) = ∑ i = s t e d C n i get(n, cnt, st, ed) = \sum_{i = st}^{ed} C_{n}^{i} get(n,cnt,st,ed)=∑i=stedCni。 e d − s t + 1 < m o d ed - st + 1< mod ed−st+1<mod。 c n t cnt cnt 是为了方便计算传上去的。
我们发现,计算 ∑ i = 0 ⌊ k + 1 m o d ⌋ − 1 C n m o d i \Large \sum_{i = 0}^{\left \lfloor \frac{k + 1}{mod} \right \rfloor - 1}C_{\frac{n}{mod}}^{i} ∑i=0⌊modk+1⌋−1Cmodni 又转化成了一个子问题。并且上下界都会除以 m o d mod mod,所以减小的会很快。
当 k < m o d k < mod k<mod 时:
r e s = ∑ i = 0 k C n i \Large res = \sum_{i = 0}^{k} C_{n}^{i} res=∑i=0kCni。
如果按照上面的式子去做,那么时间复杂度是 O ( T × m o d ) O(T \times mod) O(T×mod) 的。
因为 O ( T × m o d ) O(T \times mod) O(T×mod) 是过不去的。 瓶颈在于第一种情况中枚举 u u u 和计算 g e t get get 函数,以及第二种情况中枚举 i i i。它们的复杂度是 O ( m o d ) O(mod) O(mod) 的。我们考虑优化。
现在问题转变成了如何快速的求出 ∑ i = 0 k C n i , k < m o d \Large \sum_{i = 0}^{k} C_{n}^{i},k < mod ∑i=0kCni,k<mod 以及 快速求出 ∑ i = s t e d C n i , e d − s t + 1 < m o d \Large \sum_{i = st}^{ed} C_{n}^{i}, ed -st + 1 < mod ∑i=stedCni,ed−st+1<mod。
对于 ∑ i = 0 k C n i \Large \sum_{i = 0}^{k} C_{n}^{i} ∑i=0kCni
我们考虑用 L u c a s Lucas Lucas 计算的时候, C n i = C n % m o d i % m o d × C n m o d i m o d \Large C_{n}^{i} = C_{n \%mod}^{i\%mod} \times C_{\frac{n}{mod}}^{\frac{i}{mod}} Cni=Cn%modi%mod×Cmodnmodi。
因为 i < m o d i < mod i<mod,所以 C n i = C n % m o d i × C n m o d 0 = C n % m o d i \Large C_{n}^{i} = C_{n \% mod}^{i} \times C_{\frac{n}{mod}}^{0} = C_{n \% mod}^{i} Cni=Cn%modi×Cmodn0=Cn%modi。
我们直接预处理出来 C i j , 0 ≤ j ≤ i < m o d \Large C_{i}^{j}, 0 \leq j \leq i < mod Cij,0≤j≤i<mod,以及 S i j = ∑ k = 0 j C i k \Large S_{i}^{j} = \sum_{k = 0}^{j} C_{i}^{k} Sij=∑k=0jCik。
那么 ∑ i = 0 k C n i = S n % m o d m i n ( k , n % m o d ) \Large \sum_{i = 0}^{k} C_{n}^{i} = S_{n \% mod}^{min(k, n \% mod)} ∑i=0kCni=Sn%modmin(k,n%mod), O ( 1 ) O(1) O(1) 调用就可以。
对于 ∑ i = s t e d C n i , e d − s t + 1 < m o d \Large \sum_{i = st}^{ed} C_{n}^{i}, ed -st + 1 < mod ∑i=stedCni,ed−st+1<mod。
我们考虑此时 s t st st 一定是 m o d mod mod 的倍数,因为 s t st st 到 e d ed ed 是长度不到 m o d mod mod 的剩余那段。
有一个性质是 若 i , j < m o d i, j < mod i,j<mod, C n i + k × m o d \Large C_{n}^{i + k \times mod} Cni+k×mod 与 C n i \Large C_{n}^{i} Cni 的比值 和 C n j + k × m o d \Large C_{n}^{j + k \times mod} Cnj+k×mod与 C n j \Large C_{n}^{j} Cnj 的比值相同。 并且这个比值是 C n / m o d k \Large C_{n / mod}^{k} Cn/modk。这个可以用 Lucas 轻松证明。
那么问题就很简单了,我们求出 ∑ i = 0 e d − s t C n i \Large \sum_{i=0}^{ed-st}C_{n}^{i} ∑i=0ed−stCni,然后再乘上 C n / m o d k \Large C_{n/mod}^{k} Cn/modk 就好了。前面那个东西就是我们上面讨论的,后面那个可以用 L u c a s Lucas Lucas 定理在 l o g 2 n log_2n log2n 的时间复杂度内求出。
然后这道题就做完了,时间复杂度是 O ( T × l o g 2 n ) O(T \times log_2n) O(T×log2n)。
#include // 找规律 好题啊
using namespace std;
typedef long long LL;
const LL mod = 2333;
int T;
LL n, k, inv[mod + 10], fac[mod + 10], c[mod + 10][mod + 10];
LL sum[mod + 10][mod + 10];
LL C(int n, int m){
if(n < m) return 0;
else return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
LL Pow(LL x, LL y){
LL res = 1, k = x % mod;
while(y){
if(y & 1) res = (res * k) % mod;
y >>= 1;
k = (k * k) % mod;
}
return res % mod;
}
LL Lucas(LL n, LL m){
if(n < mod && m < mod) return C(n, m);
else return C(n % mod, m % mod) * Lucas(n / mod, m / mod) % mod;
}
LL get(LL n, LL k, LL st, LL ed){
if(st > ed) return 0;
else{
LL tmp = Lucas(n / mod, k);
return tmp * sum[n % mod][min(ed - st, n % mod)] % mod;
}
}
LL calc(LL n, LL k){// calc(n, k) = Σc[n][0] + c[n][1] + ... + c[n][k]
k = min(k, n);// 取小的
if(k < mod) return sum[n % mod][min(k, n % mod)];// k < mod 根据Lucas定理可知道, c[n][i] = c[n % mod][i % mod] * c[n / mod][i / mod] = c[n % mod][i] * c[n / mod][0] = c[n % mod][i]
else return (sum[n % mod][n % mod] * calc(n / mod, (k + 1) / mod - 1LL) % mod + get(n, (k + 1) / mod, (k + 1) / mod * mod, k)) % mod;
}
int main(){
fac[0] = 1LL;
for(int i = 1; i < mod; i++) fac[i] = fac[i - 1] * (1LL * i) % mod;
inv[mod - 1] = Pow(fac[mod - 1], mod - 2) % mod;
for(int i = mod - 2; i >= 0; i--) inv[i] = inv[i + 1] * (1LL * (i + 1)) % mod;
for(int i = 0; i <= mod - 1; i++){
for(int j = 0; j <= i; j++){
c[i][j] = C(i, j);
if(!j) sum[i][j] = c[i][j] % mod;
else sum[i][j] = (sum[i][j - 1] + c[i][j]) % mod;
}
}
scanf("%d", &T);
while(T--){
scanf("%lld%lld", &n, &k);
k = min(k, n);
if(n < mod) printf("%lld\n", sum[n][k]);
else{// n >= mod
printf("%lld\n", calc(n, k));
}
}
return 0;
}