https://vjudge.net/problem/CodeForces-359D
http://codeforces.com/problemset/problem/359/D
题目大意:
给一串数,问一个区间内所有的数是否能被其其中一个数所全部整除,求出满足条件的区间的长度最大值,并输出这样的区间的个数与它们的左端点。
换句话将,求区间GCD=区间MIN的最大长度区间。
明显st表解决。
对于最大区间长度,二分判断即可。
(因为在poj做过类似的题所以思路能很快……就是题看不懂有点难,所以特地给出翻译)
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<vector> using namespace std; const int N=1e5*3+1; inline int read(){ int X=0,w=0; char ch=0; while(ch<'0' || ch>'9') {w|=ch=='-';ch=getchar();} while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } inline int qpow(int a){ if(a==0)return 1; return (1<<a); } int gcdd[N][30]; int minn[N][30]; int log[N]; int n; int gcd(int a,int b){ if(!b)return a; return gcd(b,a%b); } void st(){ for(int j=1;j<=log[n];j++){ for(int i=1;i<=n;i++){ int p=i+qpow(j)-1; if(p>n)continue; gcdd[i][j]=gcd(gcdd[i][j-1],gcdd[i+qpow(j-1)][j-1]); minn[i][j]=min(minn[i][j-1],minn[i+qpow(j-1)][j-1]); } } return; } bool pan(int k){ for(int i=1;i<=n;i++){ int j=i+k; if(j>n)break; int l=log[k]; int p=qpow(l); int GCD=gcd(gcdd[i][l],gcdd[j-p+1][l]); int MIN=min(minn[i][l],minn[j-p+1][l]); if(GCD==MIN)return 1; } return 0; } int len; void erfen(int l,int r){ if(l>r)return; int mid=(l+r)>>1; if(pan(mid)){ len=mid; erfen(mid+1,r); }else{ erfen(l,mid-1); } return; } vector<int>ans; int main(){ n=read(); log[0]=-1; for(int i=1;i<=n;i++){ gcdd[i][0]=minn[i][0]=read(); log[i]=log[i>>1]+1; } log[0]=0; st(); erfen(0,n-1); for(int i=1;i<=n;i++){ int j=i+len; if(j>n)break; int l=log[len]; int p=qpow(l); int GCD=gcd(gcdd[i][l],gcdd[j-p+1][l]); int MIN=min(minn[i][l],minn[j-p+1][l]); if(GCD==MIN)ans.push_back(i); } printf("%d %d ",(int)ans.size(),len); for(int i=0;i<ans.size();i++){ printf("%d ",ans[i]); } return 0; }