题目描述
输入
第一行三个整数,n,m和T。
第二行n个数,第i个数表示ai。
第三行m个数,第i个数表示bi。
输出
输出一个数,答案对T取余数的结果。
样例输入
3 2 998244353
2 2 6
3 3
样例输出
80
提示
思路
不知道Legendre定理的点这里
不会实现的点这里
用了Legendre,这道题的思路就很明显了,用它把分子和分母分解一下, 再除一下,但除法无法取余,所以我们用b[i]来存i的次数,分子有的就加,分母有的就减(同一数项可以相抵消),然后再用qkp取余乘一下就行了。当然了,先用欧拉筛法把100000以内的素数筛出来。
代码大概就长这个样子:
#include <cstdio>
#include <cctype>
#include <iostream>
using namespace std;
int n, m, t, cnt, u, maxn, ans=1;
int a[100005], prime[66666];
bool vis[100005];
void Rd(int &x){
x = 0;
int f = 1; char c = getchar();
while( !isdigit(c) ) { if(c == '-') f = -1; c = getchar(); }
while( isdigit(c) ) { x=(x<<1)+(x<<3)+(c^48); c = getchar(); }
x *= f;
}
void sieve(int x){
for(int i = 2; i <= x; i ++){
if( !vis[i] )
prime[++cnt] = i;
for(int j = 1; j <= cnt && prime[j]*i <= x; j ++){
vis[prime[j]*i] = 1;
if( i % prime[j] == 0 )
break;
}
}
}
int qkp(int x, int y){
int sum = 1;
while( y ){
if( y&1 )
sum = sum%t*x%t;
y >>= 1;
x = x%t*x%t;
}
return sum;
}
int main(){
Rd(n), Rd(m), Rd(t);
sieve(100000);
for(int i = 1; i <= n; i ++){
Rd(u);
for(int j = 1; j <= cnt && prime[j] <= u; j ++){
int p = 1;
while( p*prime[j] <= u ){
p *= prime[j];
a[prime[j]] += u/p;
}
maxn = max(maxn, prime[j]);
}
}
for(int i = 1; i <= m; i ++){
Rd(u);
for(int j = 1; j <= cnt && prime[j] <= u; j ++){
int p = 1;
while( p*prime[j] <= u ){
p *= prime[j];
a[prime[j]] -= u/p;
}
maxn = max(maxn, prime[j]);
}
}
for(int i = 2; i <= maxn; i ++)
ans = ans*qkp(i, a[i])%t;
printf("%d\n", ans);
return 0;
}
极限数据时间复杂度呢大概就是O(1918400000),很明显超时,接下来我们考虑优化
优化
因为我们知道legendre的核心大概长这样:
⌊
n
p
k
⌋
\lfloor \frac{n}{p^k} \rfloor
⌊pkn⌋
但我们会发现它存在阶段性,例如
⌊
5
5
⌋
=
⌊
6
5
⌋
=
⌊
7
5
⌋
=
⌊
8
5
⌋
=
⌊
9
5
⌋
\lfloor \frac{5}{5} \rfloor=\lfloor \frac{6}{5} \rfloor=\lfloor \frac{7}{5} \rfloor=\lfloor \frac{8}{5} \rfloor=\lfloor \frac{9}{5} \rfloor
⌊55⌋=⌊56⌋=⌊57⌋=⌊58⌋=⌊59⌋
也就是说在某个范围内的
⌊
n
p
k
⌋
\lfloor \frac{n}{p^k} \rfloor
⌊pkn⌋是一定的,我们只要知到这个区间的中有多少个数,那么这个区间就有多少个
⌊
n
p
k
⌋
\lfloor \frac{n}{p^k} \rfloor
⌊pkn⌋,就不用再认认真真的搞每个数,我们求区间和就用前缀和,此时我们就用一个桶数组来存放数,再求一次前缀和就可以知道某个区间的数的个数了。
那我们如何确定区间的左右端点呢?
其实我们可以发现只要
⌊
n
m
⌋
\lfloor \frac{n}{m} \rfloor
⌊mn⌋(m>n)那么这个式子的值其实为0。
所以,我们只要枚举
⌊
n
p
k
⌋
\lfloor \frac{n}{p^k} \rfloor
⌊pkn⌋的值i,然后乘一下
p
k
{p^k}
pk,
i
∗
p
k
i*{p^k}
i∗pk就是这个区间的左端点,由上面一条我们可以知道只要
i
∗
p
k
+
m
i*{p^k}+m
i∗pk+m(
m
<
p
k
m<{p^k}
m<pk)那么它除以
p
k
{p^k}
pk的值就与
i
∗
p
k
i*{p^k}
i∗pk除以
p
k
{p^k}
pk的值是相同的,所以只要
m
m
m取最大值
p
k
−
1
{p^k}-1
pk−1就行了,化简一下右端点就可以变为
(
i
+
1
)
∗
p
k
−
1
(i+1)*{p^k}-1
(i+1)∗pk−1,即左端点+
p
k
−
1
{p^k}-1
pk−1。
⌊
n
p
k
⌋
\lfloor \frac{n}{p^k} \rfloor
⌊pkn⌋其实就是质因子
p
p
p的个数,我们再用一个数组存质因子
p
p
p的指数,处理分子的时候加,处理分母的时候减。最后再来一个qkp就行啦
希望读者提出宝贵意见,谢谢啦
参考代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
using namespace std;
#define LL long long
#define M 100005
int n, m, t, cnt, u, maxn;
int a[M], prime[10000];
LL b[M];
bool vis[M];
void Rd(int &x){
x = 0;
int f = 1; char c = getchar();
while( !isdigit(c) ) { if(c == '-') f = -1; c = getchar(); }
while( isdigit(c) ) { x=(x<<1)+(x<<3)+(c^48); c = getchar(); }
x *= f;
}
void sieve(int x){
for(int i = 2; i <= x; i ++){
if( !vis[i] )
prime[++cnt] = i;
for(int j = 1; j <= cnt && prime[j]*i <= x; j ++){
vis[prime[j]*i] = 1;
if( i % prime[j] == 0 )
break;
}
}
}
LL qkp(LL x, LL y){
LL sum = 1;
while( y ){
if( y&1 )
sum = sum%t*x%t;
y >>= 1;
x = x%t*x%t;
}
return sum;
}
int main(){
Rd(n), Rd(m), Rd(t);
sieve(100000);
for(int i = 1; i <= n; i ++){
Rd(u);
a[u]++;
maxn = max(u, maxn);
}
for(int i = 1; i <= maxn; i ++)
a[i] += a[i-1];
for(int i=1;i<=cnt&&prime[i]<=maxn;i++){
for(LL j=prime[i];j<=maxn;j*=prime[i]){
for(LL k=1;k*j<=maxn;k++){
LL l = j*k;
LL r = 1ll*min(maxn*1ll, 1ll*(l+j-1));
b[i] += (a[r]-a[l-1])*k;
}
}
}
memset(a, 0, sizeof(a));
for(int i = 1; i <= m; i ++){
Rd(u);
a[u]++;
maxn = max(maxn, u);
}
for(int i = 1; i <= maxn; i ++)
a[i] += a[i-1];
for(int i=1;i<=cnt&&prime[i]<=maxn;i++){
for(LL j=prime[i];j<=maxn;j*=prime[i]){
for(LL k=1;k*j<=maxn;k++){
LL l = 1ll*j*k;
LL r = 1ll*min(maxn*1ll, 1ll*(l+j-1));
b[i] -= (a[r]-a[l-1])*k;
}
}
}
LL ans = 1;
for(int i = 1; i <= cnt; i ++)
ans = ans*qkp(prime[i], b[i])%t;
printf("%lld\n", ans);
return 0;
}