一开始不会做,在Lynstery大佬的点拨下秒懂了……
首先推柿子:
∑j=1n[gcd(i,j)=1]aj⇒∑j=1naj∑d|(i,j)μ(d)⇒∑d|iμ(d)∑d|jaj
把 ∑d|jaj 看做是关于d的函数 f(d)
因为
aj
只对
j
的因子有贡献,修改时暴力维护
询问也一样,枚举
i
的因子
总复杂度 O(nn√)
示例程序:
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=100005,N=maxn-5;
int n,q,a[maxn],mu[maxn],p[maxn];
ll f[maxn];
vector<int> fac[maxn];
bool vis[maxn];
void prepare(){
mu[1]=1;
for (int i=2;i<=N;i++){
if (!vis[i]) p[++p[0]]=i,mu[i]=-1;
for (int j=1;j<=p[0]&&i*p[j]<=N;j++){
vis[i*p[j]]=1;
if (i%p[j]==0) {mu[i*p[j]]=0;break;}
else mu[i*p[j]]=-mu[i];
}
}
for (int i=1;i<=N;i++)
for (int j=1;i*j<=N;j++) fac[i*j].push_back(i);
for (int i=1;i<=n;i++)
for (int j=0;j<fac[i].size();j++)
f[fac[i][j]]+=a[i];
}
int main(){
scanf("%d%d",&n,&q);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
prepare();
while (q--){
int c;scanf("%d",&c);
if (c==1){
int i,b;scanf("%d%d",&i,&b);
for (int j=0;j<fac[i].size();j++)
f[fac[i][j]]+=b-a[i];
a[i]=b;
}else{
int i;scanf("%d",&i);ll ans=0;
for (int j=0;j<fac[i].size();j++)
ans+=mu[fac[i][j]]*f[fac[i][j]];
printf("%lld\n",ans);
}
}
return 0;
}

博客介绍了如何利用莫比乌斯反演解决51Nod上的题目1678 lyk与gcd。通过推导,博主展示了在维护f(d)和进行询问时可以达到O(n√n)的复杂度,给出了示例程序。
1147

被折叠的 条评论
为什么被折叠?



