HDU 5726--GCD【倍增】【单调栈】【STL-map】

来源:互联网 发布:vb调用matlab的dll 编辑:程序博客网 时间:2024/06/06 02:26

Description

Give you a sequence of N (N≤100,000)integers:a1,…,an(0<ai1000,000,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).

题目大意就是给一个数列,每次给一个l,r,求出[l,r]的gcd和与[l,r]的gcd相同的区间个数。

题解

第一问很简单吧,直接用倍增搞就好了(类似于ST表的搞法)。

看第二问。首先,这里总共会有多少个不同的gcd的值呢,答案是log2a[i]个(这非常显然吧,如果gcd要变,一定是变小,最少要除以2)。

这就告诉我们,因为gcd的总数很小,所以可以事先预处理处所有可能出现的gcd的值的答案。问题来了,如何预处理?在预处理时要充分利用gcd的总量很小这一特殊的性质。维护一个栈,表示以某一位为右端点的所有区间中每一种gcd出现的次数,考虑在右边加了一个数,以新数为右端点的序列中的gcd的值就是原来的gcd分别与新数取gcd,别忘了,新加入的数也是gcd的一种情况。至于每种gcd的个数都是很好维护的东西了。

最后,累计答案的时候要用到STL的map(每次刷的时候把以当前为右端点的所有可能情况全部累计到map里)。

代码

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<map>#define maxn 100006#define LL long longusing namespace std;inline char nc(){    static char buf[100000],*i=buf,*j=buf;    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;}inline int _read(){    char ch=nc();int sum=0;    while(!(ch>='0'&&ch<='9'))ch=nc();    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();    return sum;}map<int,LL>res;struct data{    int x;    LL num;    bool operator <(const data&b)const{return x<b.x;}}stack[maxn],stack1[maxn];int tet,n,m,top,f[maxn][19];int gcd(int x,int y){return !y?x:gcd(y,x%y);}void make_f(){    for(int j=1;j<=log2(n);j++)     for(int i=1;i<=n-(1<<j)+1;i++)      f[i][j]=gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);}int get(int l,int r){    int j=log2(r-l+1);    return gcd(f[l][j],f[r-(1<<j)+1][j]);}int main(){    freopen("gcd.in","r",stdin);    freopen("gcd.out","w",stdout);    tet=_read();    for(int t=1;t<=tet;t++){        printf("Case #%d:\n",t);        n=_read();res.clear();        for(int i=1;i<=n;i++)f[i][0]=_read();        make_f();        m=_read();top=0;        for(int i=1;i<=n;i++){            for(int j=1;j<=top;j++)stack1[j].x=gcd(stack[j].x,f[i][0]),stack1[j].num=stack[j].num;            stack1[++top].x=f[i][0];stack1[top].num=1;            sort(stack1+1,stack1+1+top);            int j=1,top1=top;top=0;            while(j<=top1){                int k=j;LL sum=0;                while(k<=top1&&stack1[k].x==stack1[j].x)sum+=stack1[k++].num;                stack[++top].x=stack1[j].x;stack[top].num=sum;res[stack[top].x]+=stack[top].num;                j=k;            }        }        while(m--){            int l=_read(),r=_read(),k=get(l,r);            printf("%d %lld\n",k,res[k]);        }    }    return 0;}