hdu5726GCD

来源:互联网 发布:c语言unicode转字符串 编辑:程序博客网 时间:2024/06/06 08:05

题目连接

题意:给n个数,再来q个询问,每次给出l,r 输出gcd(a[l][,a[l+1],........,a[r])和区间最小公约数与该最小公约数相同的区间的个数

求最小公约数gcd用倍增RMQ

定义f[i][k]为从a[i]开始长度为2^k的区间的最小公约数

那么f[i][k]=gcd(f[i][k-1],f[i+(1<<(k-1))][k-1]

举个例子:n=5,a[5]={1,2,4,6,7}

f[i][0]=a[i]; f[1][1]=gcd(a[1],a[2]);

而f[1][0]=a[1];f[2][0]=a[2];

所以f[1][1]=gcd(f[1][0],f[2][0])

所谓的倍增就是把区间不断分成两半达到nlog级别

所以我们通过倍增可以求得gcd();但后面的区间的个数怎么求呢?

gcd(l,r)有这样一个性质,gcd(l,r)>=gcd(l,r+k),此处的gcd(l,r)为区间l到r的最小公约数

因为每多一个数约束就会大一些

所以我们遍历左区间,通过二分找到和上面的这个特性找最小公约数为g的区间的个数,用mp[g]表示个数

上面这句话看不懂没关系,很正常,语言表达能力不行

下面我把思路再讲下,就很容易理解了

我们上面说了要遍历左区间,那么先g=a[l],

如果gcd(a[l],a[l+1]......,a[l+k])==g,那么是不是说明最小公约数为g的区间的个数最少有k个,所以mp[g]+=k

又因为gcd(区间范围)是单调不递增的,那么我们是不是可以用二分找到l+k

那么我们为一个gcd找到了部分的区间数,继续

那么gcd(l,l+k+1)  是不是一定是一个新的gcd,如果不是那二分找到的就是l+k+1了

同理,我们令g=gcd(l,l+k+1),如果gcd(a[l],a[l+1],...........,a[l+k+1].......a[l+p])==g,且gcd(a[l],a[l+1],...........,a[l+k+1].......a[l+p+1])!=g

我们用二分找到l+p后,那么mp[g]+=l+p-l,

直到l+p<=n结束第一个左区间的遍历

这样的一次遍历中g有多少个呢?g最多有log1000,000,000

因为a[i]最多有个log1000,000,000质因数

继续下一个左区间的遍历。。。。。。。。。。。。。

代码实现:

用cin和cout超时。。。

#include<bits/stdc++.h>using namespace std;#define ll long longconst int N=1e5+7;int t,n,q,l,r;map<int,ll> mp;int a[N];int f[N][30];int GCD(int a ,int b){   return b==0?a:GCD(b,a%b);}void RMQ(){    int i,k;    for(k=1;k<=20;k++)    for(i=1;i<=n;i++)    if(i + (1 << k)-1<=n)    f[i][k]=GCD(f[i][k-1],f[i+(1<<(k-1))][k-1]);}int query(int l,int r){    int k=(int)(log(r-l+1.0)/log(2.0));   return GCD(f[l][k], f[r - (1 << k) +1][k]);}void pre(){    int l,r,mid,pos;    for(int i=1;i<=n;i++)    {         pos=i;        while(pos<=n)        {            l=pos; r=n;            int k=query(i,pos);            while(l<r)            {                mid=(l+r+1)/2;                if (query(i,mid)<k) r=mid-1;                   else l=mid;            }            mp[k]+=(l-pos+1);  pos=l+1;        }    }}int main(){    cin>>t;    for(int ii=1;ii<=t;ii++)    {        mp.clear();        scanf("%d",&n);        for(int i=1;i<=n;i++)        {            cin>>a[i];            f[i][0]=a[i];        }        RMQ();        pre();        scanf("%d",&q);        printf("Case #%d:\n",ii);        for(int i=0;i<q;i++)        {           scanf("%d%d",&l,&r);            int ans=query(l,r);          printf("%d %lld\n",ans,mp[ans]);        }    }    return 0;}




原创粉丝点击