TrickGCD
Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 4147 Accepted Submission(s): 1509
Problem Description
You are given an array A , and Zhu wants to know there are how many different array B satisfy the following conditions?
* 1≤Bi≤Ai
* For each pair( l , r ) (1≤l≤r≤n) , gcd(bl,bl+1...br)≥2
Input
The first line is an integer T(1≤T≤10) describe the number of test cases.
Each test case begins with an integer number n describe the size of array A.
Then a line contains n numbers describe each element of A
You can assume that 1≤n,Ai≤105
Output
For the kth test case , first output "Case #k: " , then output an integer as answer in a single line . because the answer may be large , so you are only need to output answer mod 109+7
Sample Input
1 4 4 4 4 4
Sample Output
Case #1: 17
Source
2017 Multi-University Training Contest - Team 2
Recommend
liuyiding | We have carefully selected several similar problems for you: 6437 6436 6435 6434 6433
#include<bits/stdc++.h>
using namespace std;
#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)
#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
ll gcd(ll x,ll y) { return y==0?x:gcd(y,x%y); }
const int maxn =1e5+5;
const int mod=1e9+7;
/*
给定n个数,然后求gcd(a1,a2,...,an)>=2的n元组个数。
经典容斥,当然大部分容斥用莫比乌斯反演更简单些,
这道题很显然,还是老套路,枚举质数p,然后进行容斥。
但有个时间复杂度的问题,如何求
(A1/T)*(A2/T)*...*(An/T)的问题,
本人之前想到了一个超级失了智的主意
就是累成所有范围然后乘上逆元快速幂。。。
稍微举两个例子发现这个数学上有漏洞。。。
所以如何加速计算呢?
以前的分块技术用不起来的,时间复杂度还是太高,
不妨试试数据结构题目里面的思想,离散化,
离散化所有范围数字然后计数,
比如,你要计算关于质数p的区间乘积,
那么肯定是有一些Ai,Ai+1,Ai+2,,,这些数字除以p
其除数一样,我们要找的就是有多少个这样的数字。
那么离散化计数,并且前缀和(积分一下),
这样对于每个枚举到的质数因子乘积,
我们倍增其关于该质数除数一样的区间,利用预处理的前缀计数,
快速幂求得贡献。
计算下时间复杂度,是O(n(logn^2)).
最后一个注意点就是,计数数组的范围要扩一倍,
在写代码时可以感觉到。。。
*/
int n,t,ub;
int seq[maxn];
///筛出素数
int vis[maxn],miu[maxn];
int prim[maxn],cnt=0;
void sieve()
{
int N=maxn-1;
memset(vis,0,sizeof(vis));
memset(miu,0,sizeof(miu));
for(int i=2;i<=N;i++)
{
if(!vis[i])
{
prim[cnt++]=i;
miu[i]=-1;
}
for(int j=0;j<cnt;j++)
{
ll k=1LL*i*prim[j];
if(k>N) break;
vis[k]=1;
if(i%prim[j]) miu[k]=-miu[i];
else break;
}
}
}
///线性求逆元
ll inv[maxn];
void get_inv() { inv[1]=1; for(int i=2;i<maxn;i++) inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod; }
///快速幂取模
ll powmod(ll x,ll y) {ll t;for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod;return t;}
///计数前缀数组
ll pref[maxn*2];
int main()
{
sieve();
get_inv();
scanf("%d",&t);
for(int ca=1;ca<=t;ca++)
{
scanf("%d",&n);
ub=maxn;
memset(pref,0,sizeof(pref));
for(int i=1;i<=n;i++)
{
scanf("%d",&seq[i]);
ub=min(seq[i],ub);
pref[seq[i]]++;
}
for(int i=1;i<maxn*2;i++) pref[i]+=pref[i-1];///前缀累加,注意区间要扩充一倍。。。
ll ans=0;
for(int i=2;i<=ub;i++)
{
if(miu[i])
{
ll s=1;
for(int j=i<<1,k=2;j<maxn;j+=i,k++) ///极具数学技巧的分段处理,枚举除以i数值相同的区间个数,然后快速幂处理。
s=s*powmod(1LL*k,pref[j+i-1]-pref[j-1])%mod;
ans=(ans-miu[i]*s+mod)%mod;
}
}
printf("Case #%d: %lld\n",ca,ans);
}
return 0;
}