HDU 5381 The sum of gcd

来源:互联网 发布:手机淘宝所在地怎么改 编辑:程序博客网 时间:2024/06/06 15:47

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和处理出以某点为左、右端点的GCD变化然后暴力使用莫队更新区间答案*/#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];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);}struct He{        int idx;        ll g;};vector<He>vl[N], vr[N];void preDeal(){        for (int i = 1; i <= n; i++)        {                if (i == 1)vl[i].push_back(He{i, a[i]});                else                {                        int curg = a[i]; //当前的GCD值                        int L = i;    //第一个左端点                        for (auto&it : vl[i - 1]) //每次都以i-1为右端点的区间开始扩展,得到以i为右端点的区间                        {                                int g = gcd(it.g, curg);                                if (g != curg)                                        vl[i].push_back(He{L, curg});                                curg = g, L = it.idx; //更新curg和L值,得到新的左端点L 和 以L为左端点,i为右端点的区间的GCD值                        }                        vl[i].push_back(He{L, curg}); //最后一个区间                }        }        for (int i = n; i >= 1; i--) //用同样的方法处理以i为左端点的所有区间        {                if (i == n)vr[i].push_back(He{i, a[i]});                else                {                        int curg = a[i];                        int R = i;                        for (auto&it : vr[i + 1])                        {                                int g = gcd(it.g, curg);                                if (g != curg)                                        vr[i].push_back(He{R, curg});                                curg = g, R = it.idx;                        }                        vr[i].push_back(He{R, curg});                }        }}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
原创粉丝点击