[2016ACM多校] HDU5726 st表 离线

来源:互联网 发布:金淘店管软件多少钱 编辑:程序博客网 时间:2024/05/21 15:00

题意

给n个数{a[i]},q个询问(l,r),与[l,r]最大公约数相同的区间有多少个。

思路

首先是查询最大公约数,然后统计相同的区间。查询可以用各种结构,既然只用查询那就用st简单高效,主要考虑统计。指定开头s,那么从s开始的区间,随着结尾t的减小,gcd减小,gcd种类不超过log(a[s])个,可以二分每种gcd结束的位置,然后累加。这样全统计时间复杂度为O(n*logn*logn),鉴于gcd的log很小不会超时。内存方面,可以用一个map记录询问了哪些gcd,然后只累加相应的结果,其他的就不管了。

AC代码 C++

#include <stdio.h>#include <algorithm>#include <map>#define MAXN 100005using namespace std;int a[MAXN];int st[MAXN][20];int ans[MAXN];inline int rmq(int l, int r){    int limit=0;    while(1 << limit+1 <= r - l + 1)        limit++;    return __gcd(st[l][limit], st[r-(1<<limit)+1][limit]);}int main(){    int T, t, n, q, i, j, k, l, r, limit;    scanf("%d", &T);    for(t=1; t<=T; t++)    {        scanf("%d", &n);        for(i=1; i<=n; i++)            scanf("%d", a+i);        for(i=1, limit=0; i<=n; i++)            st[i][0] = a[i];        while(1 << limit <= n)            limit++;        for(i=1; i<limit; i++)            for(j=1; j+(1<<i)<n+2; j++)                st[j][i] = __gcd(st[j][i-1], st[j+(1<<i-1)][i-1]);        map<int, long long> sum;        scanf("%d", &q);        for(i=0; i<q; i++)        {            scanf("%d%d", &l, &r);            sum[ans[i] = rmq(l, r)] = 0;        }        for(i=1; i<=n; i++)        {            j = i;            while(true)            {                limit = rmq(i, j);                l = j;                r = n + 1;                while(l < r)                {                    k = l + r >> 1;                    if(rmq(i, k) != limit)                        r = k;                    else                        l = k + 1;                }                if(sum.find(limit) != sum.end())                    sum[limit] += (long long)r - j;                if(r > n)                    break;                else                    j = r;            }        }        printf("Case #%d:\n", t);        for(i=0; i<q; i++)            printf("%d %I64d\n", ans[i], sum[ans[i]]);    }    return 0;}
0 0
原创粉丝点击