HDU 5381 The sum of gcd (莫队 + 二分 + 区间GCD特性)

/**
题意:....

思路: 固定一个端点(值为k),那么这个端点属于的所有区间的不同的gcd最多只有logk中,因为gcd 整除 k, 从大到小排序去重后
后一个数至少除以2,所以不同的GCD最多只有logk个,那么找出这个端点往左和往右当GCD为g时能扩展到多远,这个二分即可,区间
的gcd用ST表预处理出来,之后找出关系式后莫队算法就可以了
*/


#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e4 + 10;
using namespace std;

int n, m, T, kase = 1;
int a[maxn], Q, block[maxn];
int st[maxn][15];
ll ans[maxn];
struct qry {
    int b, l, r, id;
    qry() {}
    qry(int b, int l, int r, int id) : b(b), l(l), r(r), id(id) {}
    bool operator < (qry p) const {
        if(b != p.b) return b < p.b;
        return r < p.r;
    }
} q[maxn];
struct P {
    int l, r, g;
    P() {}
    P(int l, int r, int g) : l(l), r(r), g(g) {}
};
vector<P> gl[maxn], gr[maxn];
int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }

void init() {
    for(int i = 1; (1 << i) <= n; i++) {
        int sz = (1 << (i - 1)), s = (1 << i);
        for(int x = 1; x < n; x++) {
            if(x + s - 1 > n) break;
            int j = x + sz;
            st[x][i] = gcd(st[x][i - 1], st[j][i - 1]);
        }
    }
}

int query(int l, int r, int val) {
    int tot = (r - l + 1), i = 0;
    while((1 << i) <= tot) i++; i--;
    for( ; i >= 0; i--) if(l + (1 << i) - 1 <= r) {
        int x = st[l][i]; l = l + (1 << i);
        int y = x / val;
        if(x - val * y) return 0;
    }
    return 1;
}

void init_block(int siz) {
    int num = 1, cnt = 1;
    while(num < n + 5) {
        for(int i = 0; i < siz && num < n + 5; i++) block[num++] = cnt;
        cnt++;
    }
}

ll calculate_sum(ll i, ll l, ll r) {
    ll sum = 0;
    for(ll x = 0; x < gl[i].size(); x++) {
        P info = gl[i][x];
        ll ans = info.g;
        ll L = max(l, (ll)info.l), R = min(r, (ll)info.r);
        if(L > R) break; sum += ans * (R - L + 1);
    }
    for(ll x = 0; x < gr[i].size(); x++) {
        P info = gr[i][x];
        ll ans = info.g;
        ll L = max(l, (ll)info.l), R = min(r, (ll)info.r);
        if(L > R) break; sum += ans * (R - L + 1);
    }
    return sum - a[i];
}

void solve(ll &las_ans, ll lasl, ll lasr, ll nowl, ll nowr, int id) {
    while(lasl < nowl) { las_ans -= calculate_sum(lasl, lasl, lasr); lasl++; }
    while(lasl > nowl) { lasl--; las_ans += calculate_sum(lasl, lasl, lasr); }
    while(lasr < nowr) { lasr++; las_ans += calculate_sum(lasr, lasl, lasr); }
    while(lasr > nowr) { las_ans -= calculate_sum(lasr, lasl, lasr); lasr--; }
    ans[id] = las_ans;
}

inline bool scan_d(int &num)
{
        char in;bool IsN=false;
        in=getchar();
        if(in==EOF) return false;
        while(in!='-'&&(in<'0'||in>'9')) in=getchar();
        if(in=='-'){ IsN=true;num=0;}
        else num=in-'0';
        while(in=getchar(),in>='0'&&in<='9'){
                num*=10,num+=in-'0';
        }
        if(IsN) num=-num;
        return true;
}

int main() {
    scan_d(T);
    while(T--) {
        scan_d(n);
        int bck = sqrt(n) + 1;
        for(int i = 1; i <= n; i++) {
            scan_d(a[i]); block[i] = i / bck;
            st[i][0] = a[i];
            gl[i].clear(); gr[i].clear();
        }
        init();
        for(int i = 1; i <= n; i++) {
            int lasg = a[i], from = i;
            while(from) { ///log * log * log ???
                int l = 1, r = from, g = gcd(lasg, a[from]);
                while(l < r) {  ///log
                    int mid = (l + r) >> 1;
                    int flag = query(mid, from, g);
                    if(!flag) l = mid + 1;
                    else r = mid;
                }
                gl[i].push_back(P(l, from, g));
                from = l - 1; lasg = g;
            }
            from = i; lasg = a[i];
            while(from < n + 1) {
                int l = from, r = n + 1, g = gcd(lasg, a[from]);
                while(l < r) {
                    int mid = (l + r) >> 1;
                    int flag = query(from, mid, g);
                    if(flag) l = mid + 1;
                    else r = mid;
                }
                gr[i].push_back(P(from, r - 1, g));
                from = r; lasg = g;
            }
        }
        scan_d(Q);
        for(int i = 1; i <= Q; i++) {
            int l, r;
            scan_d(l); scan_d(r);
            q[i] = qry(block[l], l, r, i);
        }
        sort(q + 1, q + 1 + Q);
        q[0] = qry(0, 1, 1, 0);
        ll nowl, nowr, lasl = 1, lasr = 1, las_ans = a[1];
        for(int i = 1; i <= Q; i++) {
            nowl = (ll)q[i].l; nowr = (ll)q[i].r;
            solve(las_ans, lasl, lasr, nowl, nowr, q[i].id);
            lasl = nowl; lasr = nowr;
        }
        for(int i = 1; i <= Q; i++) printf("%lld\n", ans[i]);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值