HDU4767_Sum Of Gcd

通过一个题目,学到了很多。

题意为给你n个数,每次询问i,j,答案为i,j间任取两数所有的取法gcd的和。

假设我们当前要看看这个区间有多少个数的gcd为x,最最原始的想法都是查询这个区间有多少个数为x的倍数。然后任取两个%&*¥#@!,但是有的gcd不是x,而是x的倍数。

这个问题这样来考虑,其实思想上是莫比乌斯反演。

假设当前我们有x个数是要求的现在的数y的倍数,那么所有的y的约数也肯定包含了这x个数。

所以我们可以当前要乘以的这个数不是x,而是一个f[x],显然x的所有的约数的函数和为x,这样就保证了求解的正确性。

同时想到了这里就会发现,所求的函数值与原题给的数据和各个数字的位置关系是没有关系的了,可以预处理。

最后看过各种,终于学到了,这个函数值就是欧拉函数值。  神坑啊。

还有就是这里的区间求解有一个小小的技巧。

区间分块处理,什么意思呢?

把区间排序,每次按照区间左端点所在的区进行排序,如果在同一区的话,就按右端点来排序,这样就保证了更新的复杂度为n*sqrt(n)。

真心受教了,不过鄙人还是觉得这种做法不太严谨,感觉如果是这个题目有极限数据的话,还是会T,不过确实比我想过的所有其他的想法都要优。

数论又一次被虐了。。。。。。

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <cmath>
#define maxn 20002
typedef long long ll;
using namespace std;

vector<int> g[maxn];
ll sum[maxn],ans;
int num[maxn],a[maxn],phi[maxn],t,n,m,Q,cas=0,curl,curr;

struct query{
    int li,ri,pos;
}q[maxn];

bool cmp(query q1,query q2)
{
    int l1=q1.li/m,l2=q2.li/m;
    if (l1==l2) return q1.ri<q2.ri;
    return q1.li<q2.li;
}

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

void add(int x,int tag)
{
    for (unsigned i=0; i<g[x].size(); i++)
    {
        int k=g[x][i];
        if (tag==-1) num[k]--;
        ans+=(ll)tag*num[k]*phi[k];
        if (tag== 1) num[k]++;
    }
}

void _process()
{
    ans=0;
    memset(num,0,sizeof num);
    curl=q[1].li,curr=q[1].ri;
    for (int i=curl; i<=curr; i++) add(a[i],1);
    sum[q[1].pos]=ans;
    for (int i=2; i<=Q; i++)
    {
        while (curl<q[i].li) add(a[curl],-1),curl++;
        while (curr>q[i].ri) add(a[curr],-1),curr--;
        while (curl>q[i].li) add(a[--curl], 1);
        while (curr<q[i].ri) add(a[++curr], 1);
        sum[q[i].pos]=ans;
    }
}

int main()
{
    init();
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d",&n);    m=(int)sqrt(n+0.5);
        for (int i=1; i<=n; i++) scanf("%d",&a[i]);
        scanf("%d",&Q);
        for (int i=1; i<=Q; i++) scanf("%d%d",&q[i].li,&q[i].ri),q[i].pos=i;
        sort(q+1,q+1+Q,cmp);
        _process();
        printf("Case #%d:\n",++cas);
        for (int i=1; i<=Q; i++) printf("%I64d\n",sum[i]);  
    }
    return 0;
}

 

posted @ 2013-11-30 20:42  092000  阅读(406)  评论(0编辑  收藏  举报