HDU 5726 GCD
对于第一问:无修改的查询区间GCD,可以采用RMQ倍增的思想。
第二问:可以预处理。暴力枚举左端点L。GCD从左到右是递减的,并且肯定是有一些段是一样的值,值的种类最多只有log(1000, 000, 000)种,因此可以二分确定每一段的范围。然后用map统计一下即可。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<map> #include<set> #include<queue> #include<stack> #include<iostream> using namespace std; typedef long long LL; const int maxn=100000+10; int T,n,a[maxn],dp[maxn][30]; map<int,LL>M; int gcd(int a,int b) { if(b==0) return a; return gcd(b,a%b); } void RMQ_init() { for(int i=0;i<n;i++) dp[i][0]=a[i]; for(int j=1;(1<<j)<=n;j++) for(int i=0;i+(1<<j)-1<n;i++) dp[i][j]=gcd(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); } int RMQ(int L,int R) { int k=0; while((1<<(k+1))<=R-L+1) k++; return gcd(dp[L][k],dp[R-(1<<k)+1][k]); } void pre() { M.clear(); for(int i=0;i<n;i++) { int p=i; while(p<n) { int ll=p,rr=n-1,pos; while(ll<=rr) { int mid=(ll+rr)/2; if(RMQ(i,mid)<RMQ(i,p)) rr=mid-1; else pos=mid,ll=mid+1; } M[RMQ(i,p)]=M[RMQ(i,p)]+(LL)(pos-p+1); p=pos+1; } } } int main() { scanf("%d",&T); int cas=0; while(T--) { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); RMQ_init(); pre(); int q; scanf("%d",&q); printf("Case #%d:\n",++cas); for(int i=1;i<=q;i++) { int L,R; scanf("%d%d",&L,&R); L--,R--; printf("%d %lld\n",RMQ(L,R),M[RMQ(L,R)]); } } return 0; }