题目连接
题意
给定一个长为n的数组A,求解一定条件下能构造多少个不同数组B。条件为
1≤Bi≤Ai
和 对于任意的
l,r(1≤l≤r≤n)
,
gcd(Bl,Bl+1,...,Br)≥2
.
分析
显然gcd最小的情况必然是在
l=1,r=n
时取得,所以仅考虑
l=1,r=n
的情况即可。考虑枚举
gcd(B1,B2,...,Bn)
的结果。举例
gcd(B1,B2,...,Bn)=2
,
B1
对这个方案的种类贡献为
b1=B1/2
,那么总方案数为
∏ni=1bi
,容斥处理掉重复计算的结果即可。接下来问题是怎么快速求取
bi
。暴力尝试A数组的每个数显然时间上不可行。于是考虑枚举
bi=j
,查询存在几个数在这个范围内。赛时一直算不清复杂度,忘了这样处理的复杂度为
O(n×ln(n))
,一直纠结于一个
O(n32)
的算法。
代码
using namespace std;
const
int N =
100000 +
10;
const
int mod =
1e9 +
7;
int T, n, a[N], ca[N
*2], inv[N] = {
0,
1}, cnt[N], vis[N];
int mn, mx;
long long quick_pow(long long a,long long b){
long long ret=
1;
while(b){
if(b&
1)
ret=(ret
*a)
%mod;
b>>=
1;
a=(a
*a)
%mod;
}
return ret;
}
bool isS
qr[N];
int lowbit(
int x){
return x&-
x;
}
void prime() {
memset(isSqr,
0, sizeof(isSqr));
for(
int i=
2;i<=
100000;i++)
{
if(cnt[i])
continue;
for(
int j=i;j<=
100000;j+=i) {
cnt[j]++;
if(j % (i
*i) ==
0) isS
qr[j] =
1;
}
}
}
int getnum(
int k){
long long ret=
1;
for(
int i=
1;i<=
100000;++i){
if(k
*i>mx)
break;
int num=ca[k
*(i+
1)-
1]-ca[k
*i-
1];
if(num==
0)
continue;
ret
*=quick_pow(i,num);
if(ret>mod)
ret
%=mod;
}
return ret;
}
int main()
{
for(
int i=
2;i<N;i++)
inv[i] = (mod - mod/i)
*1ll* inv[mod
%i] % mod;
prime();
scanf(
"%d", &T);
for(
int ica=
1;ica<=T;ica++)
{
scanf(
"%d", &n);
memset(ca,
0,sizeof(ca));
mn=
100000,mx=
0;
for(
int i=
1;i<=n;i++)
{
scanf(
"%d", &a[i]);
mn = min(mn, a[i]);
mx = max(mx,a[i]);
ca[a[i]]++;
}
if(mn ==
1) {
printf(
"Case #%d: 0\n", ica);
continue;
}
for(
int i=
2;i<N
*2;++i)
ca[i]+=ca[i-
1];
int ans =
0;
int func =
1;
for(
int i=
2;i<=mn;i++)
{
func=getnum(i);
if(isS
qr[i])
continue;
if(cnt[i] %
2) ans += func;
else ans -= func;
ans
%=mod;
}
ans=(ans+mod)
%mod;
printf(
"Case #%d: %d\n", ica, ans);
}
}