HDU 5726 GCD(RMQ+二分,详解)

来源:互联网 发布:Java restpath 编辑:程序博客网 时间:2024/04/28 13:39

题目链接:HDU 5726


题面:

GCD

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1151    Accepted Submission(s): 354


Problem Description
Give you a sequence of N(N100,000) integers : a1,...,an(0<ai1000,000,000). There are Q(Q100,000) queries. For each query l,r you have to calculate gcd(al,,al+1,...,ar) and count the number of pairs(l,r)(1l<rN)such that gcd(al,al+1,...,ar) equal gcd(al,al+1,...,ar).
 

Input
The first line of input contains a number T, which stands for the number of test cases you need to solve.

The first line of each case contains a number N, denoting the number of integers.

The second line contains N integers, a1,...,an(0<ai1000,000,000).

The third line contains a number Q, denoting the number of queries.

For the next Q lines, i-th line contains two number , stand for the li,ri, stand for the i-th queries.
 

Output
For each case, you need to output “Case #:t” at the beginning.(with quotes,t means the number of the test case, begin from 1).

For each query, you need to output the two numbers in a line. The first number stands forgcd(al,al+1,...,ar) and the second number stands for the number of pairs(l,r) such that gcd(al,al+1,...,ar) equal gcd(al,al+1,...,ar).
 

Sample Input
151 2 4 6 741 52 43 44 4
 

Sample Output
Case #1:1 82 42 46 1
 

Author
HIT
 

Source
2016 Multi-University Training Contest 1
 

题意:

    给定一个序列,多次询问,每次问一个区间内的数的最小公约数,和整个区间内有多少个区间的公约数为这个值。


解题:

    看到数据量表示懵逼,不敢下手,实际上一个数x的因子数最多只有log(x),区间的GCD是递减的,而且可以用RMQ,nlogn预处理得到,并用O(1)(算是O(1)吧)询问。

大致思路是这样的,枚举左端点,确定一个右端点(开始位置为当前左端点),然后二分寻找另一个最远的右端点,使得该右端点到左端点区间内的最大公约数和原右端点到左端点的最大公约数相同,用map计数,记录整个序列内公约数为某值的个数。最后询问时,区间公约数O(1)查询,相同公约数区间数O(logn)查询。


代码:

#include <iostream>#include <cstdio>#include <cstring>#include <map>#define LL long long#define sz 100010using namespace std;int a[sz],n,d[sz][20];int gcd(int a,int b){    if(a==0)return b;    return gcd(b%a,a);}//RMQ初始化void init(){   for(int i=1;i<=n;i++)       d[i][0]=a[i];   for(int j=1;(1<<j)<=n;j++)       for(int i=1;i+(1<<j)-1<=n;i++)           d[i][j]=gcd(d[i][j-1],d[i+(1<<(j-1))][j-1]);}//伪O(1)询问int RMQ(int l,int r){    int k=0;    while((1<<(k+1))<=r-l+1)k++;    return gcd(d[l][k],d[r-(1<<k)+1][k]);}//计数,爆intmap <int,LL> cnt;int main(){    int t,le,rr,ri,ll,v,am,q,x,y;    scanf("%d",&t);    for(int ix=1;ix<=t;ix++)    {        cnt.clear();        scanf("%d",&n);        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        init();        for(int i=1;i<=n;i++)        {//ll为当前左端点            ll=i;//rr为当前右端点            rr=i;            while(rr<=n)            {//le,ri为二分左右端点                le=rr;                ri=n;                v=RMQ(ll,rr);                while(le<=ri)                {                    int mid=(le+ri)>>1;                    if(RMQ(ll,mid)>=v)                        le=mid+1;                    else                        ri=mid-1;                }//加上区间长度                cnt[v]+=le-rr;//下一次从当前枚举的最右右端点后一个位置开始                rr=le;            }        }        scanf("%d",&q);        printf("Case #%d:\n",ix);        for(int j=0;j<q;j++)        {            scanf("%d%d",&x,&y);            v=RMQ(x,y);            printf("%d %lld\n",v,cnt[v]);        }    }    return 0;}


0 0
原创粉丝点击