RMQ+二分——GCD ( HDU 5726 ) ( 2016 Multi-University Training Contest 1 1004 )

来源:互联网 发布:国际软件学院 编辑:程序博客网 时间:2024/06/06 04:42
  • 题目链接:
    http://acm.hdu.edu.cn/showproblem.php?pid=5726

  • 分析:
    给出一串数,然后进行区间查询,每次查询一个区间的gcd,并且要求同时输出和这个区间gcd值相等的其它区间的个数。

  • 题解:
    1.RMQ快速区间查询:(此处查询的是GCD)

int dp[Maxn][21];//dp[i][j]表示从 number[i]开始,长度为2^j次的区间的最值void init(){    for (int i = 0; i < N; i++) //dp前的预处理,把数组读进去        dp[i][0] = numbers[i];    for (int j = 1; (1<<j) <= N; j++)//RMQ的初始化操作    {        for (int i = 0; i+(1<<j)-1 < N; i++)        {            dp[i][j] = __gcd (dp[i][j-1], dp[i+(1<<(j-1))][j-1]);        }    }}int query(int l,  int r) // 查询区间最值{    int k = 0;    while ((1<<(k+1)) <= r-l+1)        k++;    return __gcd (dp[l][k], dp[r-(1<<k)+1][k]);}

2.二分处理相同GCD区间个数:

map <int, long long> gg; // first记录gcd值, second 记录相同gcd值下的区间个数void solve (){    for (int i = 0; i < N; i++)   //枚举左端点    {        int pos = N-1;        while (pos >= i)   //二分选定右端点        {            int tmp = query(i, pos);            int l = i, r = pos;            while (r-l > 1)            {                int mid = (l+r)>>1;                if (query(i, mid) == tmp) r = mid;                else l = mid;            }            int cur;            if (query(i, l) == tmp)                cur = l;            else                cur = r;            gg[tmp] += (pos-cur+1); // 表示区间[i, pos] 与 区间[i, cur+1]的GCD值相等,且等于tmp,所以gcd值等于tmp的数量由pos-cur+1            pos = cur-1;        }    }}
  • 参考代码:
#include<iostream>#include<cstring>#include<cstdio>#include<map>#include<algorithm>#define Maxn 112345using namespace std;typedef long long LL;LL numbers[Maxn];int dp[Maxn][21];   //dp[i][j]表示从 number[i]开始,长度为2^j次的区间的最值map <int, long long> gg; // first记录gcd值, second 记录相同gcd值下的区间个数int N;void init(){    for (int i = 0; i < N; i++) //dp前的预处理,把数组读进去        dp[i][0] = numbers[i];    for (int j = 1; (1<<j) <= N; j++)//RMQ的初始化操作    {        for (int i = 0; i+(1<<j)-1 < N; i++)        {            dp[i][j] = __gcd (dp[i][j-1], dp[i+(1<<(j-1))][j-1]);        }    }}int query(int l,  int r) // 查询区间最值{    int k = 0;    while ((1<<(k+1)) <= r-l+1)        k++;    return __gcd (dp[l][k], dp[r-(1<<k)+1][k]);}void solve (){    for (int i = 0; i < N; i++)   //枚举左端点    {        int pos = N-1;        while (pos >= i)   //二分选定右端点        {            int tmp = query(i, pos);            int l = i, r = pos;            while (r-l > 1)            {                int mid = (l+r)>>1;                if (query(i, mid) == tmp) r = mid;                else l = mid;            }            int cur;            if (query(i, l) == tmp)                cur = l;            else                cur = r;            gg[tmp] += (pos-cur+1); // 表示区间[i, pos] 与 区间[i, cur+1]的GCD值相等,且等于tmp,所以gcd值等于tmp的数量由pos-cur+1            pos = cur-1;        }    }}int main(){    int t;    cin >> t;    int tt = 1;    while(t--)    {        printf ("Case #%d:\n", tt++);        gg.clear ();        scanf("%d", &N);        for(int i=0;i<N;i++)        {            scanf("%lld", &numbers[i]);        }        init();        solve ();        int Q;        scanf("%d", &Q);        while (Q--)        {            int l , r;            scanf("%d%d", &l, &r);            int ans = query(l-1, r-1);            printf ("%d %lld\n", ans, gg[ans]);        }    }    return 0;}
0 0