题意:给定数列A,问有多少个数列B满足一下条件
1.1<=Bi<=Ai
2.对于任意 1<=l<=r<=len(A) , 有gcd(Bl,Bl+1,……Br)>=2
全是就是gcd(B)>=2
思路:
实在太弱了,多校中写不出来,就想着找最小的Ai,然后遍历每个gcd遍历一遍数组A, 1E10的复杂度 会爆
看了大佬们的博客,
有些还用了莫比乌斯反演
有些没用直接求,再容斥原理去重
一直在想为什么将数列中的数字转化为权值数组,即存值小于X的数有多少个可以把 n^2 降到 nlogn
因为 j*i-1~j*i+i-1的数值除i得到的结果都是相同的
枚举 gcd后 复杂度一共为 n/1 + n/2 + n/3 + ...........=nlogn
容斥这一块
不用莫比乌斯函数的话,普通的容斥原理,ans[a]-=(ans[2a]+ans[3a]+ans[4a]+......),得到的答案才是gcd=a对答案的贡献
同样ans[2a]-=(ans[4a]+ans[6a]+ans[8a]+......)
显然要先得到gcd=2a的ans[2a]才能得到 gcd=a的ans[a],因此要从后往前遍历容斥
#include<bits/stdc++.h>
#define N 1000000007
using namespace std;
long long f(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1) ans=(ans*a)%N;
a=(a*a)%N;
b>>=1;
}
return ans;
}
int num[100009];
long long pp[100009];
long long ans[100009];
int main()
{
int k=1;
int t,len,a;
scanf("%d",&t);
while(t--)
{
scanf("%d",&len);
memset(num,0,sizeof(num));
for(int i=0;i<len;i++)
{
scanf("%d",&a);
num[a]++;
}
for(int i=1;i<=100000;i++)
num[i]+=num[i-1];
bool flag=false;
for(int i=2;i<=100000;i++)//枚举gcd
if(flag) ans[i]=0;
else
{
if(num[i-1]>0)
{
flag=true;
ans[i]=0;
continue;
}
ans[i]=1;
for(int j=1;j*i<=100000;j++)
{
ans[i]=ans[i]*(f(j,num[min((j+1)*i-1,100000)] - num[j*i-1]))%N;
}
}
long long p=0;
for(int i=100000;i>=2;i--)
{
for(int j=2*i;j<=100000;j+=i)
{
ans[i]-=ans[j];
ans[i]=(ans[i]%N+N)%N;
}
p = (p+ans[i])%N;
}
printf("Case #%d: %lld\n",k++,p);
}
return 0;
}
莫比乌斯函数
这里分享两个博客感觉挺不错的
根据容斥原理,我们知道,ans = +[k=一个不同素数之积 时对答案的贡献]
= -[k=两个不同素数之积 时对答案的贡献]
= +[k=三个不同素数之积 时对答案的贡献]
比如上面那个图,ans=1*F[2]+1*F[3]+1*F[5]+(-1)*F[6]+(-1)*F[10]+(-1)*F[15]+ 1*F[30]+。。。。。。
刚好是莫比乌斯函数的相反数
感觉就像一位大佬说的那样 (-1)^K ,n=p1p2p3……pk 这实际上就是在容斥
#include<bits/stdc++.h>
using namespace std;
#define N 1000000007
using namespace std;
long long f(long long a,long long b)
{
long long ans=1;
while(b)
{
if(b&1) ans=(ans*a)%N;
a=(a*a)%N;
b>>=1;
}
return ans;
}
int mu[100005];
int num[100005];
bool book[100005];
int prime[100005];
void init() //莫比乌斯函数表
{
memset(book,false,sizeof(book));
mu[1] = 1;
int cnt = 0;
for(int i = 2;i <= 100000;i++)
{
if(!book[i]) //第一个遇见的素数
{
prime[cnt++] = i; //素数表
mu[i] = -1;
}
for(int j = 0;j < cnt && i*prime[j] <=100000;j++)
{
book[i*prime[j]] = true;
if(i % prime[j]) mu[i*prime[j]] = -mu[i];
else
{
mu[i*prime[j]] = 0;
break;
}
}
}
}
int main()
{
int k=1;
init();
int t,len,a,Min;
scanf("%d",&t);
long long ans,temp;
while(t--)
{
Min=100000;
scanf("%d",&len);
memset(num,0,sizeof(num));
for(int i=0;i<len;i++)
{
scanf("%d",&a);
num[a]++;
Min=min(Min,a);
}
for(int i=1;i<=100000;i++)
num[i]+=num[i-1];
ans=0;
for(int i=2;i<=Min;i++)
{
if(!mu[i]) continue;
temp=1;
for(int j=1;j*i<=100000;j++)
temp=temp*(f(j,num[min((j+1)*i-1,100000)] - num[j*i-1]))%N;
ans=ans-mu[i]*temp; //莫比乌斯函数相反数
ans=(ans%N+N)%N;
}
printf("Case #%d: %lld\n",k++,ans);
}
return 0;
}