HDU

题意:

  给出一个长度为N的序列。Q次询问,每次询问一段区间的gcd值以及有多少区间的gcd值等于该值。

题解:

  预处理st表。枚举左边界二分右边界求出所有gcd值的情况数。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
int t;
int n, q;
int dp[maxn][20];
int ql[maxn], qr[maxn];
map<int, ll> m;
int gcd(int x, int y) {
    return y==0?x:gcd(y, x%y);
}
int GCD(int l, int r) {
    int x = log2(r-l+1);
    return gcd(dp[l][x], dp[r-(1<<x)+1][x]);
}
int main() {
    scanf("%d", &t);
    for(int casee = 1; casee <= t; casee++) {
        m.clear();
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &dp[i][0]);
        for(int j = 1; (1<<j) <= n; j++)
        for(int i = 1; i+(1<<j)-1 <= n; i++)
        dp[i][j] = gcd(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
        scanf("%d", &q);
        for(int i = 1; i <= q; i++) {
            scanf("%d%d", &ql[i], &qr[i]);
            int gg = GCD(ql[i], qr[i]);
            m[gg] = 0;
        }
        for(int i = 1; i <= n; i++) {
            int r = n;
            while(r >= i) {
                int l = i, nr = r;
                int now = GCD(l, r);
                while(l <= r) {
                    int mid = l+r>>1;
                    if(GCD(i, mid) > now) l = mid+1;
                    else r = mid-1;
                }
                if(m.find(now) != m.end()) m[now] += nr-r;
            }
        }
        printf("Case #%d:
", casee);
        for(int i = 1; i <= q; i++) {
            int gg = GCD(ql[i], qr[i]);
            printf("%d %lld
", gg, m[gg]);
        }
    }
} 
View Code
原文地址:https://www.cnblogs.com/Pneuis/p/8921776.html