6053 TrickGCD(莫比烏斯反演+容斥思想+分塊字首和技巧)
阿新 • • 發佈:2019-01-06
題目大意:
給你一個數組 A ,問你有多少不大於 A 的陣列 B 使得 B 中所有元素的最大公因數不為1。(陣列 B 不大於陣列 A 就等價於,對於任意 A 陣列中的元素 a [ i ] 和 B 陣列中對應元素 b [ i ] ,均有:a [ i ] >= b [ i ])
思路:
容斥思想:該問題就可以轉化成求有多少陣列 B 滿足:B 中的所有元素的最大公因數為 1。
莫比烏斯反演:
設:
那麼顯然:
那麼根據莫比烏斯反演公式可知:
那麼我們最後要求的是
分塊字首和技巧:
顯然可知:
但是我們如果樸素的方法求每一個
那麼我們想辦法再繼續優化一下上式:
ps:這裡的sum(i)表示的是: A 陣列中有多少數除 x 向下取整為 i 。
那麼現在我們設:
下面給出
ps:
如此可將時間複雜度降為:
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define maxn 100500
#define MOD 1000000007
#define mod(x) ((x)%MOD+MOD)%MOD
long long int a[maxn],mu[maxn],num[maxn];
int cas=1,n,m,vis[maxn],prime[maxn];
void init_mu()
{
memset(vis,0,sizeof(vis));
mu[1] = 1;
int cnt = 0;
for(int i=2; i<maxn; i++)
{
if(!vis[i])
{
prime[cnt++] = i;
mu[i] = -1;
}
for(int j=0; j<cnt&&i*prime[j]<maxn; j++)
{
vis[i*prime[j]] = 1;
if(i%prime[j]) mu[i*prime[j]] = -mu[i];
else
{
mu[i*prime[j]] = 0;
break;
}
}
}
}
long long int fast_pow(long long int s,long long int x)
{
long long int ans=1;
while(x>0)
{
if(x&1)
{
ans*=s;
ans=mod(ans);
}
x>>=1;
s*=s;
s=mod(s);
}
return mod(ans);
}
long long int F(int x)
{
long long int ans=1;
for(int i=0;i<=maxn/x;i++)
{
ans*=fast_pow((long long int)i,num[min(x*(i+1),maxn)-1]-num[min(x*i,maxn)-1]);
ans=mod(ans);
}
return mod(ans);
}
int main()
{
int T;
scanf("%d",&T);
init_mu();
while(T--)
{
m=maxn<<1;
scanf("%d",&n);
memset(num,0,sizeof(num));
for(int i=0;i<n;i++)
{
scanf("%lld",&a[i]);
num[a[i]]++;
if(a[i]<m)m=a[i];
}
for(int i=1;i<maxn;i++)
{
num[i]+=num[i-1];
}
long long int ans=0;
for(int i=2;i<=m;i++)
{
if(mu[i]==0)continue;
if(mu[i]==1)ans-=F(i);
else ans+=F(i);
ans=mod(ans);
}
ans=mod(ans);
printf("Case #%d: %lld\n",cas++,mod(ans));
}
}