admin管理员组文章数量:1612099
题意:请问在a数组里满足的对数的个数也就是求然后对于每一个询问x都输出一个值代表对数的个数
思路:莫比乌斯反演。既然确定了是莫反,那么就要开始给函数赋予意义了,辣么首先另我们需要求的结果为
然后我们就可以求
到这一步之后我们来仔细看看这个式子
首先这个式子要求的是gcd为x的倍数的对子
也就是说此时的里都包含因子d,又因为d可以整除x所以可以整除x,也就是说我们这个F(x)所存储的值为在整个a数组中存在可以整除x的对数的个数
然后通过反演我们可以得到这样的一个式子:这个式子就是直接通过反演得来的
在莫反中,的值是可以求出来的,那么接下来我们只需要搞定F(d)的值即可,由上面的推导可以知道,F(d)的含义就是gcd为d的倍数的对子的个数.对于这个值我们发现如果的gcd值是d的倍数当且仅当都是d的倍数
所以我们只需要预处理出的个数即可,我们记这个数量为h(d)所以
所以最终这个式子就可以变成这个美丽的样子:
写到了这一步如果是一般的问题就已经解决了,但是在这里我们还需要一步来优化时间复杂度,因为在这里我们如果直接暴力遍历每一个x和d那么时间复杂度为显然是会超时的
那么如何优化呢,这是我的思路:
首先,为什么之前需要遍历x和d因为在我们这个式子里它不仅仅有x也有(d/x)这种形式,所以我们迫不得已必须遍历两个变量且都是1e5级别的,所以我们需要思考怎么让它的变量减少最好两个变量统一,那么我们开始思考如过前面的x变成(d/x)的话,我们的x|d需要变成什么?
我们遍历每一个x的倍数,因为i*x必定可以整除i的因子,然后存在h数组里,表示在这些a数组中有多少个可以整除j的数字的个数。
最终的式子就变成了:
len为最大的倍数
所以最终我们的伪代码就是
for i:1-->m
ll X=i*x;
记录下最大的能存在的倍数len
for j:遍历每一个i的因子
h[j]+=a[X]的数量
for i:1-->len
ans+=h[i]*h[i]*mu[i];
h[i]=0;//记得请空h数组,因为每一次都是不一样的
最后上代码:
#include <algorithm>
#include <vector>
#include <string>
#include <map>
#include <queue>
#include <stack>
#include <queue>
#include <iostream>
#include <cmath>
#include <cstring>
#include <set>
#include <iomanip>
#include <stdexcept>
#include <fstream>
#define ll long long
#define endl '\n'
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+7;
const double pi=acos(-1);
int prime[N];
bool vis[N];
int mu[N];
ll n,m,k;
ll ans[N],num[N],h[N];
void init(){
mu[1]=1;
int cnt=0;
memset(vis,0,sizeof(vis));
for(int i=2;i<=N;i++)
{
if(!vis[i]){
prime[++cnt]=i;
mu[i]=-1;
}
for(int j=1;j<=cnt&&i*prime[j]<=N;j++)
{
vis[i*prime[j]]=1;
if(i%prime[j]==0){
break;
}
else mu[prime[j]*i]=-mu[i];
}
}
}
vector<int>v[N];
void getyinshu(){
for(int i=1;i<N;++i){
for(int j=1;j*j<=i;++j){
if(i%j==0){
v[i].push_back(j);
if(j*j!=i) v[i].push_back(i/j);//存下所有i的因子
}
}
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
init();
getyinshu();
int t;
cin>>t;
while(t--){
cin>>n>>m>>k;
for(int i=1;i<N;++i){
num[i]=0;
ans[i]=-1;
}
vector<int>a(n+1);
for(int i=1;i<=n;++i){
cin>>a[i];
num[a[i]]++;//统计每个数的数量
}
ll len;
while(k--){
int x;
cin>>x;
if(ans[x]!=-1){
cout<<ans[x]<<endl;
continue;
}
for(int i=1;i<=m;++i){
ll X=i*x;//从1开始遍历每一个倍数的X值
if(X>m) break;
len=i;
for(auto j:v[i]){
h[j]+=num[X];//因为一个数X的值为i*x时它就可以整除所有的i的因子,所以遍历i所有的因子
//h数组存下所有包含j因子的数的数量
}
}
ll res=0;
for(ll i=1;i<=len;++i){//len存的是最大的倍数
res+=h[i]*h[i]*mu[i];
h[i]=0;
}
ans[x]=res;//记忆化,避免重复遍历
cout<<ans[x]<<endl;
}
}
return 0;
}
版权声明:本文标题:Problem H. Curious(莫反) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1728630917a1167071.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论