「NOIP模拟赛」数字对
「题目描述」
小H是个善于思考的学生,现在她又在思考一个有关序列的问题。
她的面前浮现出一个长度为n的序列{ai},她想找出一段区间[L, R](1 <= L <= R <= n)。
这个特殊区间满足,存在一个k(L <= k <= R),并且对于任意的i(L <= i <= R),ai都能被ak整除。这样的一个特殊区间 [L, R]价值为R – L。
小H想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。
「输入格式」
第一行,一个整数n.
第二行,n个整数,代表ai.
「输出格式」
第一行两个整数,num和val,表示价值最大的特殊区间的个数以及最大价值。
第二行num个整数,按升序输出每个价值最大的特殊区间的L.
「样例输入1」
5
4 6 9 3 6
「样例输出1」
1 3
2
「样例输入2」
5
2 3 5 7 11
「样例输出2」
5 0
1 2 3 4 5
「数据范围」
30%: 1 <= n <= 30 , 1 <= ai <= 32.
60%: 1 <= n <= 3000 , 1 <= ai <= 1024.
80%: 1 <= n <= 300000 , 1 <= ai <= 1048576.
100%: 1 <= n <= 500000 , 1 <= ai < 2 ^ 31.
题解
30% :暴力枚举判断。O(n^4) or O(n^3)。
60% :特殊区间的特点实际上就是区间最小值等于这个区间的GCD,于是暴力或递推算出每个区间最小值与GCD。而对于最大价值,可以通过二分来进行求解。复杂度O(n ^ 2)。
100%:在60%的基础上,最小值与GCD都使用RMQ算法来求解,对于这道题推荐使用ST表,线段树会被艹飞。复杂度O(nlogn)。
实际上此题数据水机智的暴力踩标程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define ll long long #define inf 2147483647 using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int bin[20]; int n,top,res; int a[500005],L[500005],q[500005],ans[500005]; int mn[500005][19],g[500005][19]; int gcd(int x,int y) { return y==0?x:gcd(y,x%y); } void pre() { for(int i=1;i<=n;i++) mn[i][0]=g[i][0]=a[i]; for(int j=1;j<=18;j++) for(int i=1;i<=n;i++) { if(i+bin[j]-1<=n) { mn[i][j]=min(mn[i][j-1],mn[i+bin[j-1]][j-1]); g[i][j]=gcd(g[i][j-1],g[i+bin[j-1]][j-1]); } else break; } } bool query(int l,int r) { int t=L[r-l+1],R=r-bin[t]+1; return min(mn[l][t],mn[R][t])==gcd(g[l][t],g[R][t]); } void solve(int x) { for(int i=1;i<=n;i++) if(i+x-1<=n) if(query(i,i+x-1)) ans[++top]=i; } int main() { //freopen("pair.in","r",stdin); //freopen("pair.out","w",stdout); bin[0]=1;for(int i=1;i<20;i++)bin[i]=bin[i-1]<<1; L[0]=-1;for(int i=1;i<=500000;i++)L[i]=L[i>>1]+1; n=read(); for(int i=1;i<=n;i++)a[i]=read(); pre(); int l=1,r=500000; while(l<=r) { int mid=(l+r)>>1; top=0; solve(mid); if(top)res=mid,l=mid+1; else r=mid-1; } top=0; solve(res); printf("%d %d\n",top,res-1); for(int i=1;i<=top;i++) printf("%d ",ans[i]); return 0; } |