题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=6053
题目大意:
给定数组A,
Ai
为
Bi
的最大取值范围,求问有多少种不同的序列B满足任意
[l,r]
区间内,gcd值不为1
分析:
gcd值不为1,即所有数都不互质,均拥有某个质因子,但是可以发现直接容斥的复杂度是
O(105)∗O(105)=O(1010)
,显然复杂度超标,需要优化,经过学长指导,通过对
Ai/p
的结果进行,分组,可以用每次一次
O(log2n)
复杂的二分和一次
O(log2n)
的快速幂求得一个分组的答案,每个因子均摊大约
log2n
个分组,一共至多n个因子,所以总计复杂度为
O(n(log2n)2)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+200;
const int mod = 1e9+7;
int n;
ll ans;
bool isprime[maxn];
ll pf[maxn];
int pn[maxn];
ll arr[maxn];
void init()
{
memset(isprime,true,sizeof(isprime));
for (int i = 2; i <= maxn ; i ++)
{
if (isprime[i])
{
for (int j = i ; j <= maxn ; j+= i)
{
if(!pf[j]) pf[j] = i;
else pf[j] *= i;
pn[j]++;
if (j!=i) isprime[j] = false;
}
}
}
}
ll qp(ll a ,int n)
{
ll res = 1;
while (n)
{
if (n&1) res= res*a%mod;
a = a*a %mod;
n >>= 1;
}
return res;
}
ll solve(int p)
{
int i = 1;
ll res = 1;
while (i<=n)
{
int l = i,r = n+1 ,mid;
int x = arr[i]/p;
while (1)
{
if (l==r-1)
break;
mid = (l+r)>>1;
if (x==arr[mid]/p)
l = mid;
else
r = mid;
}
res = res*qp(x,l-i+1)%mod;
i = l +1;
}
return res%mod;
}
int main() {
int T,t=1;
init();
scanf("%d",&T);
while (T--)
{
ans = 0;
scanf("%d",&n);
for (int i = 1 ; i <= n ; i ++)
scanf("%lld",&arr[i]);
sort(arr+1,arr+n+1);
int up = arr[1];
for (int i = 2 ; i <= up ; i ++)
{
if (i!=pf[i]) continue;
ll tmp = solve(i);
if (pn[i]&1) ans += tmp%mod;
else ans -= tmp%mod;
ans %=mod;
ans = (ans+mod)%mod;
}
printf("Case #%d: %lld\n",t++,ans%mod);
}
return 0;
}