P4345 [SHOI2015]超能粒子炮改
題目描述
曾經發明瞭腦洞治療儀與超能粒子炮的發明家 SHTSC 又公開了他的新發明:超能粒子炮・改——一種可以發射威力更加強大的粒子流的神祕裝置。
超能粒子炮・改相比超能粒子炮,在威力上有了本質的提升。它有兩個引數 n,k ,它會向每個編號為 0 到 k (包含兩端)的位置i發射威力為 \(C_{n}^{i} \bmod 2333\)的粒子流。
現在 SHTSC 給出了他的超能粒子炮・改的引數,讓你求出其發射的粒子流的威力之和除以 2333所得的餘數。
輸入格式
第一行一個整數 t表示資料組數。
之後 t 行,每行兩個整數 n、k,含義如題面描述。
輸出格式
t 行,每行一個整數,表示其粒子流的威力之和模 2333 的值。
輸入輸出樣例
輸入 #1
3
5 5
10 7
1145 14
輸出 #1
32
968
763
說明/提示
對於 10% 的資料, t=1 , \(n,k \le 1000\) ;
對於 30% 的資料, t=1 ,\(n,k \le 1000000\);
對於 50% 的資料, t=1, \(n \le 10^{18},k \le 1000\);
對於 70% 的資料, t=100, \(n,k \le 10^{18}\);
對於 100% 的資料,t=100000 , \(n,k \le 10^{18}\).
首先,這道題可以用盧卡斯加大力吸氧得到50分。
然後,無論怎麼調都會TLE。
那麼我們就要開始想正解。
我們要求的是這個柿子
\(\sum_{i=0}^{k}C_{n}^{i} \bmod 2333\)
我們可以用Lucas定理換一下變成
\(\sum_{i=0}^{k}C_{n/p}^{i/p}C_{n\bmod p}^{i\bmod p}\bmod p\)
然後,我們就會神奇的發現,\(C_{n}^{i \bmod p}\)重複計算了好多次
我們發現從0-p-1到p-2p-1.....他們中間都有一段連續的\(\sum_{i=0}^{p-1}C_{n}{i}\)
這樣,我們就可以先計算這些段出現了多少次
就是 \(\sum_{i=0}^{p-1}C_{n}^{i} \times \sum_{i=0}^{k/p-1} C_{n/p}^{i}\)
對於剩下的一些散塊,我們直接可以套用盧卡斯定理來求。
也就是 \(\sum_{i=0}^{k \bmod p}C_{n \bmod p}^{i}\times C_{n/p}^{k/p}\)
對於這個\(\sum_{i=0}^{p-1}C_{n}^{i}\)柿子,我們可以通過預處理出[0,p-1]的組合數的字首和搞出來。
\(\sum_{i=0}^{k/p-1} C_{n/p}^{i}\)這個柿子呢,我們就可以直接遞迴處理。
總柿子
\(\sum_{i=0}^{k}C_{n}^{i} \bmod 2333\)
=\(\sum_{i=0}^{k}C_{n/p}^{i/p}C_{n\bmod p}^{i\bmod p}\bmod p\)
=\(\sum_{i=0}^{p-1}C_{n}^{i} \times \sum_{i=0}^{k/p-1} C_{n/p}^{i}\)+ \(\sum_{i=0}^{k \bmod p}C_{n \bmod p}^{i}\times C_{n/p}^{k/p}\)
=\(f(n \bmod p,p-1) \times f(n \bmod p,k/p-1) \times C_{n/p}^{k/p} \times f(n \bmod p,k \bmod p)\)
其中 \(f(x,y)\) = \(\sum_{i = 0}^{y}C_{x}^{i}\)
程式碼
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const int p = 2333;
LL t,a,b,c[2550][2550],f[2550][2550];
inline LL read()
{
LL s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
return s * w;
}
LL Lucas(LL n, LL m)//Lucas定理求組合數
{
if(m == 0) return 1;
if(n < m) return 0;
return c[n%p][m%p] * Lucas(n/p,m/p) % p;
}
LL calc(LL n,LL k)
{
if(k < 0) return 0;
if(n == 0 || k == 0) return 1;
if(n < p && k < p) return f[n][k];
return (f[n%p][p-1] * calc(n/p,k/p-1) % p + Lucas(n/p,k/p) * f[n%p][k%p] % p) % p;//遞迴處理
}
void YYCH()//預處理出0-p的組合數
{
c[0][0] = f[0][0] = 1;
for(int i = 1; i <= 2500; i++)//楊輝三角求組合數
{
c[i][0] = c[i][i] = 1;
for(int j = 0; j <= i; j++)
{
c[i][j] = (c[i-1][j-1] + c[i-1][j]) % p;
}
}
for(int i = 0; i <= 2500; i++) f[i][0] = 1;
for(int i = 0; i <= 2500; i++)//組合數的字首和
{
for(int j = 1; j <= 2500; j++)
{
f[i][j] = (f[i][j-1] + c[i][j]) % p;
}
}
}
int main()
{
t = read(); YYCH();
while(t--)
{
a = read(); b = read();
printf("%lld\n",calc(a,b));
}
return 0;
}