[Luogu1890]gcd区间

原题链接https://www.luogu.org/problem/show?pid=1890

  1. 暴力中的暴力。
    对于每一组询问l..r,我们先循环暴力枚举l..r中最大值到1,再暴力循环l..r的每一个数,判断前一重循环能否整除后一重,如果全部都能,则可判定它就是l..r的最大公因数。
    时间复杂度\(O(mn*maxnum)\)。如果带入如果带入极值,需要执行\(10^{18}\)次。果断超时。

  2. 稍微优化的暴力。
    对于每一组询问,我们循环l..r的每一个数,然后再像这样:ans=gcd(ans,a[i]);。ans初值为a[l]。
    这样的时间复杂度\(O(mn*logmaxnum)\)。代入极值:\(1000*1000000*30=3*10^{10}\)。还是会超时.

核心代码:

ans=a[l];
for(int j=l;j<=r;++j)
    ans=gcd(ans,a[j]);

3.动态规划
设f[i][j]表示a[i..j]的最大公因数,则我们有状态转移方程:

\[f[i][j]=gcd(f[i][j-1],a[j]) \]

这个方程是十分好推的。初值是这样的:

\[f[i][i]=a[i] \]

计算dp的时间是\(O(n^2)\),回答询问要\(O(1)\),共m次询问,有\(O(n^2+m)\)的总时间复杂度。最多需要执行\(2*10^6\)次。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define rep(i,a,n) for(register int i=(a);i<=(n);++i)
#define per(i,a,n) for(register int i=(a);i>=(n);--i)
#define fec(i,x) for(register int i=head[x];i;i=Next[i])
#define debug(x) printf("debug:%s=%d\n",#x,x)
#define mem(a,x) memset(a,x,sizeof(a))
template<typename A>inline void read(A&a){a=0;int f=1,c=0;while(c<'0'||c>'9'){c=getchar();if(c=='-')f*=-1;}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}a*=f;}
template<typename A,typename B>inline void read(A&a,B&b){read(a);read(b);}
template<typename A,typename B,typename C>inline void read(A&a,B&b,C&c){read(a);read(b);read(c);}
template<typename A>A gcd(const A&m,const A&n){return m%n==0?n:gcd(n,m%n);}

const int maxn=1000+7;
int n,m,a[maxn],f[maxn][maxn],l,r;

void dp(){
	rep(i,1,n)rep(j,i+1,n)f[i][j]=gcd(f[i][j-1],a[j]);
}

void Init(){
	read(n,m);
	rep(i,1,n)read(a[i]),f[i][i]=a[i];
}

void Work(){
	dp();
	rep(i,1,m){
		read(l,r);
		printf("%d\n",f[l][r]);
	}
}

int main(){
	Init();
	Work();
	return 0;
} 
posted @ 2017-08-01 16:17  hankeke303  阅读(183)  评论(0编辑  收藏  举报