hdu5726 GCD ST表+离线

来源:互联网 发布:基金公司招聘条件 知乎 编辑:程序博客网 时间:2024/04/30 15:34
给定一个序列,每次询问一个区间 
输出这个区间上所有数的GCD,以及GCD与其相同的区间个数(整个序列)

一个连续区间的GCD,用倍增法预处理一下,就能做到 O(1)查询 
对于相同区间计数,就把询问先离线一下 
枚举区间左端点,区间GCD是随右端点递减的,并且是阶梯式的 
并且由于GCD递减的很快,这样一个阶梯只有几层,可以当作log的 
所以对于每一个GCD,二分右端点,就能求出答案
#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cmath>#include <vector>#include <map>using namespace std;#define sint long long#define maxn 110000int gcd(int a,int b){    if(a>b) swap(a,b);    if(a==0) return b;    return gcd(b%a,a);}map<int,sint>mp;int n,m,gd[maxn][20];int query(int l,int r){    int k=(int)log2((double)(r-l+1));    return gcd(gd[l][k],gd[r-(1<<k)+1][k]);}void update(int pos){    int nowgcd=gd[pos][0];    int togcd=query(pos,n);    int margin=pos,ll,rr,mid,ans;    while(nowgcd!=togcd)    {        ll=margin+1;        rr=n;        ans=-1;        while(ll<=rr)        {            mid=(ll+rr)>>1;            if(query(pos,mid)!=nowgcd)            {                ans=mid;                rr=mid-1;            }            else ll=mid+1;        }        mp[nowgcd]+=ans-margin;        margin=ans;        nowgcd=query(pos,margin);    }    if(margin<=n)    {        mp[togcd]+=(n-margin)+1;    }}void solve(){    mp.clear();    for(int i=1;i<=18;i++)    {        for(int j=1;j<=n&&j+(1<<i)-1<maxn;j++)        {            gd[j][i]=gcd(gd[j][i-1],gd[j+(1<<(i-1))][i-1]);        }    }    for(int i=1;i<=n;i++)    {        update(i);    }    scanf("%d",&m);    int l,r;    for(int i=1;i<=m;i++)    {        scanf("%d%d",&l,&r);        int g=query(l,r);        printf("%d %lld\n",g,mp[g]);    }}int main(){    int cas;    scanf("%d",&cas);    for(int i=1;i<=cas;i++)    {        printf("Case #%d:\n",i);        memset(gd,0,sizeof(gd));        scanf("%d",&n);        for(int j=1;j<=n;j++) scanf("%d",&gd[j][0]);        solve();    }    return 0;}/*151 2 4 6 741 52 43 44 4Case #1:1 82 42 46 1*/

0 0
原创粉丝点击