您的位置:首页 > 其它

HDU4676 Sum Of Gcd (数论 + 分块)

2016-10-07 14:25 519 查看
 

∑i=LR∑j=i+1Rgcd(a[i],a[j])=∑i
4000
=LR∑j=i+1R∑d|gcd(a[i],a[j])ϕ(d)=∑dϕ(d)∑i=L,d|a[i]R∑j=i+1,d|a[j]R1

后一项就是[L,R]这段区间内有多少个d的倍数。 
cnt[d][x]代表前x项中d的倍数的个数。 
原式可化为 

∑i=LR∑j=i+1Rgcd(a[i],a[j])=∑dϕ(d)∗(cnt[d][R]−cnt[d][L−1])

如果每一次都是直接求,显然不能满足复杂度,也会爆内存,因为只是一道无修改的查询,自然想到某该的莫队!!! 
需要维护的就是该区间内cnd[d]的值。 
不妨假设[L,R]到[L−1,R]这个区间。用cnt[d]表示[L,R]之间d的倍数。 
发现该区间比上一个区间多了一个∑Ri=Lgcd(a[L−1],a[i])。

由上面的反演可得到: 

∑i=LRgcd(a[L−1],a[i])=∑d|a[L−1]ϕ(d)∗∑i=L,d|a[i]R1=∑d|a[L−1]ϕ(d)∗cnt[d]

此时就可以做出答案,当然需要找出a[L−1]的所有约数。如果a[i]<=106那么a[i]最多有100个约数,那么每一次暴力枚举需要o(100),但是这个常数很难达到。 

最大的复杂度是o(nn−−√∗100)
但是因为100很难达到,所有可行

///******************

 n的所有因数的欧拉函数的和等于n 
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<algorithm>
#include<string.h>
#include <vector>
using namespace std;

#define LL long long
#define INF 0x3f3f3f3f

vector<int> G[41000];
int phi[41000];
void init()
{
for(int i=1;i<21000;i++)
for(int j=i;j<21000;j+=i)
G[j].push_back(i);
phi[1]=1;
for(int i=2;i<21000;i++)
{
if(phi[i]==0) phi[i]=i;
else continue;
for(int j=i;j<21000;j+=i)
{
if(phi[j]==0) phi[j]=j;
phi[j] = phi[j]/i * (i-1);
}
}
}

int n,m,k;
int T;

struct Node
{
int l,r,id;
} node[41000];

int len;

bool cmp(Node a,Node b)
{
if(a.l/len!=b.l/len)
return a.l<b.l;
return a.r<b.r;
}

long long re;
long long ans[41000];
int num[41000];
int a[41000];

void add(int pos,int val)
{
int sz = G[pos].size();
for(int i=0;i<sz;i++)
{
int v = G[pos][i];
if(val==-1)
num[v]--;
re = re + (long long) val*phi[v] * num[v];
if(val==1) num[v]++;
}
}

void solve()
{
int l=0,r=0;

re=0;
memset(num,0,sizeof(num));
for(int i=0;i<m;i++)
{
while(l<node[i].l)
{
add(a[l],-1);
l++;
}
while(l>node[i].l)
{
l--;
add(a[l],1);
}

while(r<node[i].r)
{
r++;
add(a[r],1);
}
while(r>node[i].r)
{
add(a[r],-1);
r--;
}

ans[node[i].id]=re;
}
}

int main()
{
int cas=1;
init();
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
len = sqrt(n);
a[0]=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%d%d",&node[i].l,&node[i].r);
node[i].id=i;
}

sort(node,node+m,cmp);

solve();

printf("Case #%d:\n",cas++);
for(int i=0;i<m;i++)
{
printf("%I64d\n",ans[i]);
}

}
return 0;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: