GCD HDU

来源:互联网 发布:深圳软件企业认定 编辑:程序博客网 时间:2024/05/22 06:30

点击打开链接

第一问区间gcd好求 难在第二问要求整个序列有多少个区间的gcd与之相等

只查询不修改 用rmq要比线段树简单高效一些 因为第二问的范围是整个序列 所以需要需处理 就是统计所有出现的gcd值

首先要知道 两个数求gcd 除非两数相等 否则只会越来越小且至少变小2倍 满足一定单调性

所以确定一个左端点 l 再向右找一个右端点 r 使这个区间的gcd等于左端点出的数值 即[l,l] [l,l+1] [l,l+2] ... [l,r] 这些小区间的gcd值都一样 而找右端点可以根据上述的单调性来二分log(n)查找 然后再找下一个这样的区间

可能有人会有疑问 如果固定左端点后 每次移动右端点得到的gcd值都不同 都变成一个新的区间 那不就成o(n)了吗

在上面还说了 两个数求gcd 不相等则至少变小2倍 这样gcd很快就变成 1 了 所以根据这个性质知道 整个序列内最多就有log(n)个区间给你查 总体复杂度就是n*(log(n)^2)

#include <bits/stdc++.h>using namespace std;#define lll long longmap <int,lll> mp;int dp[100010][20];int num[100010];int n;int getgcd(int a,int b){    int t;    while(b!=0)    {        t=b;        b=a%b;        a=t;    }    return a;}void rmq(){    int i,j;    for(i=1;i<=n;i++)    {        dp[i][0]=num[i];    }    for(j=1;(1<<j)<=n;j++)    {        for(i=1;i+(1<<j)-1<=n;i++)        {            dp[i][j]=getgcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);        }    }    return;}int query(int l,int r){    int p;    p=0;    while((1<<(p+1))<=r-l+1)    {        p++;    }    return getgcd(dp[l][p],dp[r-(1<<p)+1][p]);}void init(){    int i,l,r,ll,rr,m,p;    for(i=1;i<=n;i++)    {        l=i,r=i;        while(r<=n)        {            ll=r,rr=n;            p=query(l,r);            while(ll<=rr)            {                m=(ll+rr)/2;                if(query(l,m)>=p)                {                    ll=m+1;                }                else                {                    rr=m-1;                }            }            mp[p]+=ll-r;            r=ll;        }    }    return;}int main(){    int t,cas,q,i,l,r,ans;    scanf("%d",&t);    for(cas=1;cas<=t;cas++)    {        mp.clear();        scanf("%d",&n);        for(i=1;i<=n;i++)        {            scanf("%d",&num[i]);        }        rmq();        init();        scanf("%d",&q);        printf("Case #%d:\n",cas);        while(q--)        {            scanf("%d%d",&l,&r);            ans=query(l,r);            printf("%d %lld\n",ans,mp[ans]);        }    }    return 0;}

原创粉丝点击