HDU 5726 GCD 求给定序列中与查询段相等的GCD个数

一上来RMQ,以便于把查询等东西都弄成logn的。然后预处理,因为GCD有递减趋势,并且每次改变下降都减少至少一半,所以很快就会到1,剩下的直接加进去;如果很慢到1,那么中途相等的gcd就可以二分。此复杂度为2logn,暴力起始位置,每次向左移都是logn,所以复杂度是2nlogn。

下面上代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <vector>
#include <set>
#include <map>
using namespace std;
map<int ,long long> mp;
int n,T,ncas=0;
int dp[200100][25];
int fun(int pos,int val)
{
    int l=1,r=pos;
    while (l<r)
    {
        int mid=(l+r)>>1;
        int len=pos-mid+1;
        int now=-1;
        int x=pos;
        for (int k=20;k>=0;k--)
        {
            if (len&(1<<k))
            {
                int temp=dp[x][k];
                if (now==-1)
                    now=temp;
                else
                    now=__gcd(now,temp);
                x-=(1<<k);
            }
        }
        if (now<val)
            l=mid+1;
        else r=mid;
    }
    return (l+r)>>1;
}
int main()
{
    scanf ("%d",&T);
    while (T--)
    {
        mp.clear();
        scanf ("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf ("%d",&dp[i][0]);
        }
        for (int k=1;(1<<k)<=n;k++)//RMQ记录当前点向左1<<k的gcd
        {
            for (int i=n;i>=1;i--)
            {
                int zuo=i-(1<<k)+1;
                if (zuo<1) break;//向左没这么多数
                int j=i-(1<<(k-1));
                dp[i][k]=__gcd(dp[i][k-1],dp[j][k-1]);//是上一层两段的和
            }
        }
        for (int i=1;i<=n;i++)
        {
            int all=-1;
            for (int j=i;j>=1;j--)
            {
                int temp=dp[j][0];
                int you=j;
                if (all==-1)
                    all=temp;
                else
                    all=__gcd(all,temp);
                j=fun(i,all);//从这个位置向左找gcd相等的位置
                int len=you-j+1;//这一段的gcd相等,都加进去
                if (mp.find(all)==mp.end()) mp[all]=0;//一个gcd对应一堆个数
                mp[all]+=1ll*len;//防爆
            }
        }
        printf ("Case #%d:\n",++ncas);
        int Q;
        scanf ("%d",&Q);
        while (Q--)
        {
            int l,r;
            scanf ("%d%d",&l,&r);//
            int len=r-l+1;
            int x=r;
            int ans=-1;
            for (int k=20;k>=0;k--)//跳着gcd
            {
                if (len&(1<<k))
                {
                    if (ans==-1)
                        ans=dp[x][k];
                    else
                        ans=__gcd(ans,dp[x][k]);
                    x-=(1<<k);//一次少一大半
                }
            }
            printf ("%d %lld\n",ans,mp[ans]);
        }
    }
    return 0;
}

 

posted on 2016-07-20 13:32  very_czy  阅读(233)  评论(0编辑  收藏  举报

导航