HDU_5381 The sum of gcd(数论+线段树)

The sum of gcd

Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
Problem Description

You have an array A,the length of A A A is n n n
Let f ( l , r ) = ∑ i = l r ∑ j = i j g c d ( a i , a i + 1 , . . . , a j ) f(l,r)=\sum_{i=l}^r\sum_{j=i}^jgcd(a_i,a_{i+1},...,a_j) f(l,r)=i=lrj=ijgcd(ai,ai+1,...,aj)

Input

There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
First line has one integers n
Second line has n integers Ai
Third line has one integers Q,the number of questions
Next there are Q lines,each line has two integers l,r
1 ≤ T ≤ 3 1≤T≤3 1T3
1 ≤ n , Q ≤ 1 0 4 1≤n,Q≤10^4 1n,Q104
1 ≤ a i ≤ 1 0 9 1≤a_i≤10^9 1ai109
1 ≤ l < r ≤ n 1≤l<r≤n 1l<rn

Output

For each question,you need to print f ( l , r ) f(l,r) f(l,r)

Sample Input

2
5
1 2 3 4 5
3
1 3
2 3
1 4
4
4 2 6 9
3
1 3
2 4
2 3

Sample Output

9
6
16
18
23
10

题意

有序列 A A A长度为 n n n,对于每个询问,求 f ( l , r ) = ∑ i = l r ∑ j = i r g c d ( a i , a i + 1 , . . . , a j ) f(l,r)=\sum_{i=l}^r\sum_{j=i}^rgcd(a_i,a_{i+1},...,a_j) f(l,r)=i=lrj=irgcd(ai,ai+1,...,aj)

思路:

区间 [ l , r ] [l,r] [l,r]内所有子区间的gcd 的和。
离线处理。所有询问以r为主键排列。从1到n依次插入 a i a_i ai,对于 1 ≤ j ≤ i 1\le j\le i 1ji,求出区间 [ j , i ] [j,i] [j,i]的gcd,并将其加到j上。当r == i时,求出 [ l , r ] [l,r] [l,r]所维护的信息的和。

信息维护:考虑线段树维护信息。在插入 a i a_i ai时,对于 1 ≤ j ≤ i 1\le j\le i 1ji,区间gcd的值最多有 l o g 2 i log_2 i log2i个不同的值,且相同值之间是连续的。可以维护线性表,求出每个不同的区间gcd的值。 j ∈ [ a l , a r ] j\in[al,ar] j[al,ar]时,区间 [ j , i ] [j,i] [j,i]的gcd为x,线段树维护区间更新。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-6
  
using namespace std;
typedef long long LL;
typedef pair<int, int> P;
const int maxn = 10010;
const int mod = 1000000007;
struct node{
    int l, r, id;
    bool operator<(node b)const{
        return r<b.r;
    }
}p[maxn];
int n, a[maxn];
LL ans[maxn], b[maxn*4], lz[maxn*4];
vector<P> g1, g2;
int gcd(int a, int b);
void solve(int pos, int x);
void pushdown(int k, int l, int r);
LL query(int l, int r, int al, int ar, int k);
void Update(int l, int r, int al, int ar, int y, int k);

int main()
{
    int t, m, i, j, k, q;
    scanf("%d", &t);
    while(t--)
    {
        g1.clear();
        g2.clear();
        memset(b, 0, sizeof(b));
        memset(lz, 0, sizeof(lz));
        scanf("%d", &n);
        for(i=1;i<=n;i++)
            scanf("%d", &a[i]);
        scanf("%d", &q);
        for(i=0;i<q;i++){
            scanf("%d %d", &p[i].l, &p[i].r);
            p[i].id = i;
        }
        sort(p, p+q);
        j = 0;
        for(i=1;i<=n;i++){
            solve(i, a[i]);
            while(j<q && p[j].r == i){
                ans[p[j].id] = query(1, n, p[j].l, i, 1);
                j++;
            }
        }
        for(i=0;i<q;i++)
            printf("%lld\n", ans[i]);
    }
    return 0;
}

void solve(int pos, int x)
{
    g2.clear();
    int l, r, i, j, k;
    i = g1.size()-1, l = pos, r = pos+1;
    do{
        while(i>=0 && gcd(x,g1[i].second) == x)
            l=g1[i].first, i--;
        Update(1, n, l, r-1, x, 1);
        g2.push_back(P(l, x));
        r = l;
        //printf("l:%d x:%d\n", l, x);
        if(i>=0)x = gcd(x, g1[i].second);
    }while(i>=0);
    g1.clear();
    for(i=g2.size()-1;i>=0;i--)
        g1.push_back(g2[i]);
}

int gcd(int a, int b)
{
    return b==0?a:gcd(b, a%b);
}

void Update(int l, int r, int al, int ar, int y, int k)
{
    if(l == al &&r == ar){
        b[k] += (r-l+1LL)*y;
        lz[k] += y;
        return ;
    }
    int mid = (l+r)/2;
    pushdown(k, mid-l+1, r-mid);
    if(ar <= mid)Update(l, mid, al, ar, y, 2*k);
    else if(al > mid)Update(mid+1, r, al, ar, y, 2*k+1);
    else Update(l, mid, al, mid, y, 2*k),
            Update(mid+1, r, mid+1, ar, y, 2*k+1);
    b[k] = b[2*k]+b[2*k+1];
}

void pushdown(int k, int l, int r)
{
    if(lz[k])
    {
        lz[2*k] += lz[k];
        lz[2*k+1] += lz[k];
        b[2*k] += lz[k]*l;
        b[2*k+1] += lz[k]*r;
        lz[k] = 0;
    }
}

LL query(int l, int r, int al, int ar, int k)
{
    if(l == al && r == ar)return b[k];
    int mid = (l+r)/2;
    pushdown(k, mid-l+1, r-mid);
    if(ar<=mid)return query(l, mid, al, ar, 2*k);
    else if(al>mid)return query(mid+1, r, al, ar, 2*k+1);
    else return query(l, mid, al, mid, 2*k)+
                    query(mid+1, r, mid+1, ar, 2*k+1);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值