HDU 5381 The sum of gcd

来源:互联网 发布:c语言面向对象编程的书 编辑:程序博客网 时间:2024/06/06 05:16

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 is n
Let f(l,r)=ri=lrj=igcd(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
1T3
1n,Q104
1ai109
1l<rn
 

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

Sample Input
251 2 3 4 531 32 31 444 2 6 931 32 42 3
 

Sample Output
9616182310

上一篇是用gcd性质处理出每个端点的gcd变化位置,这一篇是用了RMQ加二分预处理出所有的gcd变化位置,然后再用莫队算法求解。
类似的RMQ二分预处理还有HDU 5726这一题。
#pragma comment(linker, "/STACK:1024000000,1024000000")#include <set>#include <map>#include <stack>#include <cmath>#include <queue>#include <cstdio>#include <bitset>#include <string>#include <vector>#include <iomanip>#include <cstring>#include <iostream>#include <algorithm>#include <functional>#define maxn 50500using namespace std;typedef long long ll;const int N = 10000 + 5;int n, m;int block;int a[N];ll ans[N];int ggcd[23][maxn*2];struct Node{        int L, R;        int id;        bool operator<(const Node&rhs)const        {                if (L / block == rhs.L / block)                {                        return R < rhs.R;                }                return L / block < rhs.L / block;        }} q[N];int gcd(int a, int b){        return (b == 0) ? a : gcd(b, a % b);}void ST(int num){        for (int i = 1; i <= num; i++)        {                ggcd[0][i] = a[i];        }        for (int i = 1; i <= log2(num); i++)                for (int j = 1; j <= num; j++)                        if (j + (1 << i) - 1 <= num)                        {                                int a = ggcd[i - 1][j], b = ggcd[i - 1][j + (1 << i >> 1)];                                ggcd[i][j]=gcd(a,b);                        }}int RMQ(int x, int y){        int k = (int) log2(y - x + 1.0);        int a = ggcd[k][x], b = ggcd[k][y - (1 << k) + 1];        return gcd(a,b);}struct He{        int idx;        ll g;};vector<He>vl[N], vr[N];void preDeal(){        ST(n);        for(int i=1;i<=n;i++)        {                int g=ggcd[0][i],j=i;                while(j<=n)                {                        int left = j,right=n,mid;                        while(left <= right)                        {                                mid =(left + right)/2;                                if(RMQ(i,mid)>=g)                                {                                        left =mid +1;                                }                                else                                {                                        right =mid -1;                                }                        }                        mid = (left+right)/2;                        vr[i].push_back(He{mid,g});                        j=mid+1;                        g=RMQ(i,j);                }        }        for(int i=n;i>=1;i--)        {                int g=ggcd[0][i],j=i;                while(j>=1)                {                        int left = 1,right=j,mid;                        while(left <= right)                        {                                mid =(left + right)/2;                                if(RMQ(mid,i)<g)                                {                                        left =mid +1;                                }                                else                                {                                        right =mid -1;                                }                        }                        mid = (left+right)/2;                        vl[i].push_back(He{mid+1,g});                        j=mid;                        g=RMQ(j,i);                }        }}ll calc(int type, int L, int R) //计算区间[L,R]的结果{        ll res = 0;        if (!type)        {                int tr = R; //当前的右端点                for (auto&it : vl[R])                        if (it.idx >= L) //如果当前区间的左端点>=L,则当前区间为[it.idx,tr]                        {                                //这里其实用到了GCD值的传递性:                                //如果L<L1<R,gcd([L,R])=g1,gcd([L1,R])=g2(g2≥g1),那么必有gcd([L,L1-1])=g1                                res += (tr - it.idx + 1) * it.g;                                tr = it.idx - 1; //更新右端点                        }                        else        //如果当前区间的左端点<L,则应该被算入的区间为[L,tr],由于它是最后一个区间了,因此break                        {                                res += (tr - L + 1) * it.g;                                break;                        }        }        else     //原理同上        {                int tl = L;                for (auto&it : vr[L])                        if (it.idx <= R)                        {                                res += (it.idx - tl + 1) * it.g;                                tl = it.idx + 1;                        }                        else                        {                                res += (R - tl + 1) * it.g;                                break;                        }        }        return res;}void solve(){        for (int i = 1; i <= n; i++)        {                vl[i].clear(), vr[i].clear();        }        block = sqrt(n); //块数        sort(q, q + m);        preDeal();        int L = 1, R = 0;//从(1, 0)点开始转移,此时res = 0        ll res = 0;        for (int i = 0; i < m; i++)        {                while (R < q[i].R)                {                        R++;//向右右端点,然后计算[L,R]区间的结果,累加给res                        res += calc(0, L, R);                }                while (R > q[i].R) //向左移动右端点                {                        res -= calc(0, L, R);                        R--;                }                while (L > q[i].L)                {                        L--;                        res += calc(1, L, R);                }                while (L < q[i].L)                {                        res -= calc(1, L, R);                        L++;                }                ans[q[i].id] = res; //转移完毕。注意,res,L,R都不要清零        }        for (int i = 0; i < m; i++)        {                printf("%I64d\n", ans[i]);        }}int main(){        int T;        scanf("%d", &T);        while (T--)        {                scanf("%d", &n);                for (int i = 1; i <= n; i++)                {                        scanf("%d", &a[i]);                }                scanf("%d", &m);                for (int i = 0; i < m; i++)                {                        scanf("%d%d", &q[i].L, &q[i].R);                        q[i].id = i;                }                solve();        }        return 0;}

0 0
原创粉丝点击