TrickGCD
Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
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
* 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
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
144 4 4 4
Sample Output
Case #1: 17
Source
2017 Multi-University Training Contest - Team 2
题目大意
给定一个数列A,要求满足条件 ,1≤Bi≤Ai,和条件,对于数列B的任意区间( l , r ) (1≤l≤r≤n)的 区间gcd>=2,的数列B的个数。
题目分析
首先,我们可以从公因子的角度考虑,枚举区间 [ 2,mina ) (mina为数列A中的最小值),这样我们可以得到所有Bi的可能取值,然后就是排列组合的问题。但是我们要注意,排列组合中有重复的情况。根据容斥原理,被我们重复使用的情况,只需要保留一种情况,减掉多余的情况即可。容斥原理,我们可以总结为一句话,奇加偶减。后面会结合例子解释。
至于判断一个因数i的所有排列组合情况,是否需要添加到最终的ans集合中,我们引入了莫比乌斯函数

(莫比乌斯函数相关内容摘自百度百科)
根据莫比乌斯函数的定义我们可以得到,所有素数的函数值μ(n)=-1,我们可以将所有以素数为公因子的B数列的所有情况加到ans集合中,然后我们会发现公因子为非素数的情况是重复出现的。
比如,6=2*3,6在公因子为2,3时都被ans++,因此要把6减掉一次(奇加偶减);
再比如,30=2*3*5,根据奇加偶减,因数为30的排列组合结果应该加上一次,具体分析一下,30可以分解为2*3*5,因子为2,3,5时ans++,因子为2*3=6,2*5=10,3*5=15时ans--,因此因子为30时ans++(奇加偶减)。
再再比如,210=2*3*5*7,因子为2,3,5,7时ans++四次,因子为2*3=6,2*5=10,2*7=14,3*5=15,3*7=21,5*7=35时ans--六次(其实就是组合数C(4,2)=6),因子为2*3*5=30,2*3*7=42,2*5*7=70,3*5*7=105时ans++四次(即C(4,3)=4),综上ans被加了两次,所以因子为210时ans--。
具体容斥原理使用莫比乌斯函数实现的过程请参考代码。
大体的解题思路有了,观察数据大小我们发现遍历因子,遍历A数列两个for循环100000*100000肯定超时,所以我们在进行一步优化。
假设公因子为5,那么比10,11,12,13,14小的5的倍数都只有两个,5和10,那么,我们是不是能把10~14都看做10使用呢?这样就可以将A数列分块操作,使用快速幂取模进行优化。
最后是我在代码中遇到的一些问题。
1.在最后第九十多行的两个for循环中要将i,j都转化为long long,因为循环里面的运算有用到i,j,用int类型会导致精度丢失
2.num[ ]数组要开到200000,题目数据只有100000,但是在94行的for循环中,虽然j*i<=100000,因为mina最大是100000,所以(j+1)*i最大也是可能超过100000的,最大能达到200000。
3.快速幂取模long long powermod(long long a,long long b),参数和返回值都要取long long,否则会精度丢失。
代码
题目大意
给定一个数列A,要求满足条件 ,1≤Bi≤Ai,和条件,对于数列B的任意区间( l , r ) (1≤l≤r≤n)的 区间gcd>=2,的数列B的个数。
题目分析
首先,我们可以从公因子的角度考虑,枚举区间 [ 2,mina ) (mina为数列A中的最小值),这样我们可以得到所有Bi的可能取值,然后就是排列组合的问题。但是我们要注意,排列组合中有重复的情况。根据容斥原理,被我们重复使用的情况,只需要保留一种情况,减掉多余的情况即可。容斥原理,我们可以总结为一句话,奇加偶减。后面会结合例子解释。
至于判断一个因数i的所有排列组合情况,是否需要添加到最终的ans集合中,我们引入了莫比乌斯函数
莫比乌斯函数完整定义的通俗表达是:
1)莫比乌斯函数μ(n)的定义域是N
2)μ(1)=1
3)当n存在平方因子时,μ(n)=0
4)当n是素数或奇数个不同素数之积时,μ(n)=-1
5)当n是偶数个不同素数之积时,μ(n)=1
定义域为 [ 1, 50 ] 的莫比乌斯函数值如下:
根据莫比乌斯函数的定义我们可以得到,所有素数的函数值μ(n)=-1,我们可以将所有以素数为公因子的B数列的所有情况加到ans集合中,然后我们会发现公因子为非素数的情况是重复出现的。
比如,6=2*3,6在公因子为2,3时都被ans++,因此要把6减掉一次(奇加偶减);
再比如,30=2*3*5,根据奇加偶减,因数为30的排列组合结果应该加上一次,具体分析一下,30可以分解为2*3*5,因子为2,3,5时ans++,因子为2*3=6,2*5=10,3*5=15时ans--,因此因子为30时ans++(奇加偶减)。
再再比如,210=2*3*5*7,因子为2,3,5,7时ans++四次,因子为2*3=6,2*5=10,2*7=14,3*5=15,3*7=21,5*7=35时ans--六次(其实就是组合数C(4,2)=6),因子为2*3*5=30,2*3*7=42,2*5*7=70,3*5*7=105时ans++四次(即C(4,3)=4),综上ans被加了两次,所以因子为210时ans--。
具体容斥原理使用莫比乌斯函数实现的过程请参考代码。
大体的解题思路有了,观察数据大小我们发现遍历因子,遍历A数列两个for循环100000*100000肯定超时,所以我们在进行一步优化。
假设公因子为5,那么比10,11,12,13,14小的5的倍数都只有两个,5和10,那么,我们是不是能把10~14都看做10使用呢?这样就可以将A数列分块操作,使用快速幂取模进行优化。
最后是我在代码中遇到的一些问题。
1.在最后第九十多行的两个for循环中要将i,j都转化为long long,因为循环里面的运算有用到i,j,用int类型会导致精度丢失
2.num[ ]数组要开到200000,题目数据只有100000,但是在94行的for循环中,虽然j*i<=100000,因为mina最大是100000,所以(j+1)*i最大也是可能超过100000的,最大能达到200000。
3.快速幂取模long long powermod(long long a,long long b),参数和返回值都要取long long,否则会精度丢失。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod = 1e9+7;
int cas, n;
int mina;
int mo[100005];//mobius函数打表
int num[200005];//数值为i的数的个数
int a[100005];
/*long long powermod(ll a, ll b)
{
long long ans=1;
while(b)
{
if(b%2==1)ans=ans*a%mod;
a=(a*a)%mod;
b/=2;
}
printf ("ans=========%d\n",ans);
return ans;
}*/
long long int PowerMod (long long a,long long b)
{
long long ans = 1;
a = a%mod;
while (b > 0)
{
if (b%2 == 1)
ans = (ans*a)%mod;
b = b/2;
a = (a*a)%mod;
}
//printf ("ans=========%d\n",ans);
return ans;
}
void mobius(int mn)
{
mo[1] = 1;
for(int i=1; i<=mn; i++)
{
for(int j=i+i; j<=mn; j+=i)
{
mo[j]-=mo[i];
}
}
}
int main()
{
//freopen( "1009in.txt" , "r" , stdin );
//freopen( "1009out2.txt" , "w" , stdout );
cas = 0;
mobius(100000);
/*for (int i=1; i<=10000; i++)
printf ("%d %d \n",i,-mo[i]);
printf("\n");*/
int T;
scanf ("%d",&T);
while (T--)
{
memset(num, 0, sizeof(num));
mina = 100005;
scanf ("%d",&n);
for (int i=1; i<=n; ++i)
{
scanf ("%d",&a[i]);
if (mina > a[i])
{
mina = a[i];
}
}
for (int i=1; i<=n; ++i)
{
num[a[i]] ++;
}
/*for (int i=0; i<=100000; i++)
printf ("%d ",num[i]);
printf ("\n");*/
for (int i=1; i<=200000; ++i)
{
num[i] += num[i-1];
//printf ("%d ",num[i]);
}
//printf ("\n");
long long sum = 0;
//printf ("mina================%d\n",mina);
for (long long int i=2; i<=mina; ++i)//遍历因数
{
long long gg = 1;
for (long long j=1; j*i<=100000; ++j)//遍历a序列中的所有数
{
gg = (gg*PowerMod(j, num[(j+1)*i-1]-num[j*i-1]))%mod;//假设i=5,求5~9,num[9]-num[5-1]
}
//printf ("%lld\n",mo[i]*gg);
sum = (sum-gg*mo[i]%mod+mod)%mod;
//printf ("s--%lld\n",sum);
}
printf ("Case #%d: %lld\n",++cas,sum);
}
return 0;
}