题目链接:TrickGCD
题目大意:给出一个长度为n的数组,让你构造出一个长度也为n的B数组,且B数组需要满足对于所有的1<=i<=n,A[i]<=B[i],且对于B数组,任意一个区间的gcd>=2,求满足的方案数
题目思路:转化一下就变成了B数组整个区间的gcd>=2,然后直接枚举这个gcd从2到min(a[i]),然后容斥算一下贡献:
当x为奇数个不同素数的积时,ans+=1*它对答案的贡献
当x为偶数个不同素数的积时,ans+=-1*它对答案的贡献
其余情况 ans+= 0*它对答案的贡献
容斥的时候发现刚好是莫比乌斯函数的相反数,然后直接套莫比乌斯函数上去,然后预处理一下每个数的贡献,贡献实际上是
(a[i]/x)∗(a[i]/x)∗(a[i]/x)∗(a[i]/x)∗(a[i]/x)∗(a[i]/x)
x为我们枚举的gcd,然后发现在某一段数的区间num/x是一致的,分块小优化一下,然后预处理每个数出现的次数,然后累加用快速幂算,然后贡献乘莫比乌斯函数就好了,具体看代码,很好的题目,num数组记得看大,不然会越界
#include <map>
#include <set>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int mod = 1e9+7;
ll prime[maxn],mob[maxn],vis[maxn],tot;
ll t,n,a[maxn],cnt[maxn],num[3*maxn];
void Mobius(){
memset(prime,0,sizeof(prime));
memset(mob,0,sizeof(mob));
memset(vis,0,sizeof(vis));
mob[1] = 1;
tot = 0;
for(ll i = 2;i < maxn; i++){
if(!vis[i]){
prime[tot++] = i;
mob[i] = -1;
}
for(ll j = 0;j < tot&&i*prime[j] < maxn;j++){
vis[i*prime[j]] = 1;
if(i%prime[j]) mob[i*prime[j]] = -mob[i];
else{
mob[i*prime[j]] = 0;
break;
}
}
}
}
ll quick_mod(ll a,ll b,ll mod)//快速幂
{
ll ans = 1;
while(b)
{
if(b&1)
ans = (ans*a)%mod;
a = (a*a)%mod;
b >>= 1;
}
return ans;
}
int main(){
scanf("%lld",&t);
Mobius();
for(int Case = 1;Case <= t;Case++){
scanf("%lld",&n);
memset(cnt,0,sizeof(cnt));
ll Min = 0x3f3f3f3f,Max = -1;
for(ll i = 1;i <= n;i++){
scanf("%lld",&a[i]);
Min = min(Min,a[i]);
Max = max(Max,a[i]);
cnt[a[i]]++;
}
num[0] = 0;
for(ll i = 1;i <= 2*Max;i++) num[i] = num[i-1]+cnt[i];
ll ans = 0;
for(ll i = 2;i <= Min;i++){
ll tmp = 1;
for(ll j = 1;j*i <= Max;j++){
tmp = (tmp*quick_mod(j,num[j*i+i-1]-num[j*i-1],mod))%mod;
}
ans = (ans-tmp*mob[i]+mod)%mod;
}
printf("Case #%d: %lld\n",Case,ans);
}
return 0;
}