一定记住这个结论!!!
还有,代码实现可以直接爆搜。。。
第一行三个整数,n,m和T。
第二行n个数,第i个数表示ai。
第三行m个数,第i个数表示bi。
输出一个数,答案对T取余数的结果。
3 2 998244353
2 2 6
3 3
80
相信大家都会了勒让德定理吧。那么这道题,首先就根据勒让德定理,用筛法筛一个素数表(埃筛和欧筛都可以)。也就有了一种爆搜解法:
(i表示需要进行阶乘运算的数的下标,如:样例的a[1] = 2;pn表示素数的总个数,prime[j]表示素数,如:prime[1] = 2)
但是,众所周知,这样是一定会超时的,因为第一层循环耗时太久。
现在,我们开始优化:为了去掉第一层循环,我们可以使用桶。怎么使用呢?我们要用桶存下每一个需要阶乘的数,到时候就可以将所有的数一同处理。我们把这个桶命名为c[i],这个桶的意义是:值为i的数的个数。就比如说样例:c[2] = 2, c[6] = 1。
再者,因为一个数n,依次除以几个连续的数再下取整的结果是有分段性的,就比如:5来除以2,3,4,5,6,7,8,9,10,11再下取整,那么商分别为:0,0,0,1,1,1,1,1,2,2。那么每个段有多长呢?一定有这么长:对于下去整商k,当除数是j时,到
除以j后下去整商等于k。我们可以将这个分段性和桶联合起来使用。
既然这是一段一段的,我们不如想出一个办法可以一次性算出某一段中有多少个数。不难想到,前缀和是最理想的,所以把桶升级一下,存前缀和。
一共三层循环:
第一层循环:从小到大枚举素数prime[i]
for (int i = 1; i <= pn; i ++)
第二层循环:从小到大枚举素数的指数,并算出
for (LL j = prime[i]; j <= MAX; j *= prime[i])
//j直接算出prime[i]的k次方
第三层循环:枚举下取整之后的商,作为除数,算出这一段中有多少个数。
因为如果边进行除法运算,边进行模运算会出错,所以我们选择存下每个素数的指数,最后除完之后,再一一进行乘方运算并进行模运算。
于是第三层循环还要做:算出这一段中有多少个数,再乘以下取整之后的商,再将这些结果累加起来,就是prime[i]的最高次数。
for (LL k = 1; k * j <= MAX; k ++){
LL l = k * j, r = min ((k + 1) * j - 1, MAX);
w[i] += (c[r] - c[l - 1]) * k;
}//w[i]指prime[i]的最高次数
OK,代码主要部分就完了,最后就是运用快速幂累乘。
#include
#include
#include
using namespace std;
#define M 100005
#define LL long long
LL ans = 1, n, m, T, a, c[M], pn, prime[M], w[M], MAX;
bool vis[M];
void seive (int x){
for (int i = 2; i <= x; i ++){
if (! vis[i]){
prime[++ pn] = i;
vis[i] = 1;
for (int j = i * 2; j <= x; j += i)
vis[j] = 1;
}
}
}
LL qkpow (LL x, LL y){
LL sum = 1;
while (y > 0){
if (y % 2 == 1)
sum = sum * x % T;
x = x * x % T;
y /= 2;
}
return sum;
}
int main (){
scanf ("%lld %lld %lld", &n, &m, &T);
seive (100000);//筛法,范围不超十万
for (int i = 1; i <= n; i ++){
scanf ("%lld", &a);
c[a] ++;
MAX = max (MAX, a);
}
for (int i = 1; i <= MAX; i ++)//前缀和处理
c[i] += c[i - 1];
for (int i = 1; i <= pn; i ++){
for (LL j = prime[i]; j <= MAX; j *= prime[i]){
for (LL k = 1; k * j <= MAX; k ++){
LL l = k * j, r = min ((k + 1) * j - 1, MAX);
w[i] += (c[r] - c[l - 1]) * k;//先分解被除数,所以最高指数要加起来
}
}
}
MAX = 0;
memset (c, 0, sizeof(c));//注意清零
for (int i = 1; i <= m; i ++){
scanf ("%lld", &a);
MAX = max (MAX, a);
c[a] ++;
}
for (int i = 1; i <= MAX; i ++)
c[i] += c[i - 1];
for (int i = 1; i <= pn; i ++){
for (LL j = prime[i]; j <= MAX; j *= prime[i]){
for (LL k = 1; k * j <= MAX; k ++){
LL l = k * j, r = min ((k + 1) * j - 1, MAX);
w[i] -= (c[r] - c[l - 1]) * k;//这次分解除数,所以最高指数要相减
}
}
}
for (int i = 1; i <= pn; i ++){
ans = (ans * qkpow (prime[i], w[i])) % T;//最后累乘
}
printf ("%lld\n", ans);
return 0;
}
Legendre、Legendre、Legendre!