Sum Of Gcd HDU 4676 (莫队)

Sum Of Gcd

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 986 Accepted Submission(s): 465

Problem Description
Given you a sequence of number a1, a2, …, an, which is a permutation of 1…n.
You need to answer some queries, each with the following format:
Give you two numbers L, R, you should calculate sum of gcd(a[i], a[j]) for every L <= i < j <= R.

Input
First line contains a number T(T <= 10),denote the number of test cases.
Then follow T test cases.
For each test cases,the first line contains a number n(1<=n<= 20000).
The second line contains n number a1,a2,…,an.
The third line contains a number Q(1<=Q<=20000) denoting the number of queries.
Then Q lines follows,each lines contains two integer L,R(1<=L<=R<=n),denote a query.

Output
For each case, first you should print “Case #x:”, where x indicates the case number between 1 and T.
Then for each query print the answer in one line.

Sample Input
1
5
3 2 5 4 1
3
1 5
2 4
3 3

Sample Output
Case #1:
11
4
0

Source
2013 Multi-University Training Contest 8

关于一个区间内两两GCD之和,我们有一个推导:
对一个序列的某个区间L,R,每个数两两之间的GCD之和为

Σd|nφ(d)×C2num(d)

d: 这个区间的所有约数
通过这个公式,我们就可以很方便的通过枚举新加进来的数字的因子进行add和del,推算一下就可以了。
这里每个数的因子和欧拉函数要与处理一下保证复杂度。

#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define dbg(x) cout<<#x<<" = "<<x<<endl
const int maxn = 1e5+5;
struct data
{
    int l,r,id;
}Q[maxn];
int pos[maxn];
int phi[maxn];
int pri[maxn];
int a[maxn];
int ans[maxn];
int Flag[maxn];
vector<int> v[maxn];
int num[maxn];
bool cmp(const data &a,const data &b)
{
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
void Getphi(int Max)//欧拉函数与因子的预处理
{
    phi[1] = 1;
    for (int i = 2; i <= Max; i ++)
    {
        if (!Flag[i])
        {
            phi[i] = i - 1;
            pri[++ pri[0]] = i;
        }
        for (int j = 1; j <= pri[0]; j ++)
        {
            if (1ll * i * pri[j] > Max) break;
            Flag[i * pri[j]] = 1;
            if (i % pri[j] == 0)
            {
                phi[i * pri[j]] = phi[i] * pri[j];
                break;
            }
            phi[i * pri[j]] = phi[i] * (pri[j] - 1);
        }
    }
    for(int i=1;i<=Max;i++)
    {
        for(int j=i;j<=Max;j+=i)
            v[j].push_back(i);
    }
}
int L=0,R=0;
long long Ans=0;
void add(int x)
{
    for(int i=0;i<v[a[x]].size();i++)
        Ans+=1LL*phi[v[a[x]][i]]*num[v[a[x]][i]];
    for(int i=0;i<v[a[x]].size();i++)
        num[v[a[x]][i]]++;
    return ;
}
void del(int x)
{
    for(int i=0;i<v[a[x]].size();i++)
        num[v[a[x]][i]]--;
    for(int i=0;i<v[a[x]].size();i++)
        Ans-=1LL*phi[v[a[x]][i]]*num[v[a[x]][i]];
    return ;
}
int main()
{
    int n,q;
    int t;
    int cnt=1;
    Getphi(20000);
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        L=0,R=0,Ans=0;
        for(int i=1;i<=n;i++) num[i]=0;
        int sz=sqrt(n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            pos[i]=i/sz;
        }
        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&Q[i].l,&Q[i].r);
            Q[i].id=i;
        }
        sort(Q+1,Q+1+q,cmp);
        for(int i=1;i<=q;i++)
        {
            while(R<Q[i].r)
            {
                R++;
                add(R);
            }
            while(L>Q[i].l)
            {
                L--;
                add(L);
            }
            while(L<Q[i].l)
            {
                del(L);
                L++;
            }
            while(R>Q[i].r)
            {
                del(R);
                R--;
            }
            ans[Q[i].id]=Ans;
        }
        printf("Case #%d:\n",cnt++);
        for(int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值