您的位置:首页 > 其它

51nod 1678 lyk与gcd mobius反演

2017-10-24 18:40 477 查看
题意:

它拥有一个n个数的数列,它想实现两种操作。

1.将ai改为b

2.给定一个数i,求所有 gcd(i,j)=1 时的 aj 的总和。

n,q<=105

思路:

gcd为1的很像mobius反演啊…

题目就是要我们求给定n,∑Nj=1aj∗[gcd(j,n)==1]

我们设F(d)为gcd== d的倍数的和,f(d) 为gcd == d的和.

显然有F(n)=∑n|df(d)由mobius反演第二种形式可得:

f(n)=∑n|du(dn)F(d)

我们要求的就是f(1),但是前提是我们这里的F(d)维护的必须是和操作2给定的i直接相关的,即gcd(i,j) == d的倍数所有的和,那么很容易知道这个j 必须是i的一个因子才可以. 所以对于每次给定的操作2,我们n√ 枚举i的因子并维护F(d),修改同理只需修改所有的因子即可.

#include<stdio.h>

using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
int prime[maxn],mu[maxn],n,a[maxn],q;
ll sum[maxn];
bool vis[maxn];
void mobius()
{
int cnt = 0;
mu[1] = 1;
for(int i =2;i < maxn;++i)
{
if(!vis[i])
prime[++cnt] = i,mu[i] = -1;
for(int j = 1;prime[j]*i < maxn;++j)
{
vis[i*prime[j]] = 1;
if(i%prime[j] == 0)
{
mu[i*prime[j]] = 0;
break;
}
mu[i*prime[j]] = -mu[i];
}
}
return ;
}
int main()
{
//freopen("in.txt","w",stdout);
mobius();
while(~scanf("%d %d",&n,&q))
{
//memset(sum,0,sizeof sum);
for(int i = 1;i <= n;++i)
scanf("%d",&a[i]);

for(int i = 1;i <= n;++i)
{
sum[i] = 0;
for(int j = i;j <= n;j += i)
sum[i] += a[j];
}
//for(int i = 1;i <= n;++i)
//      printf("%d\n",sum[i]);
while(q--)
{
int op,x,y;
scanf("%d %d",&op,&x);
if(op == 2)
{
ll ans = 0;
for(int d = 1;d*d <= x;++d)
{
if(x % d == 0)
{
ans += mu[d] * sum[d];
if(d != (x / d))
ans += mu[x/d] * sum[x/d];
}
}
printf("%lld\n",ans);
}
else
{
scanf("%d",&y);
int cha = y - a[x];
a[x] = y;
for(int d = 1;d * d <= x;++d)
{
if(x % d == 0)
{
sum[d] += cha;
if(d != x / d)
sum[x/d] += cha;
}
}
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: