2018.10.30 NOIp模拟赛T2 数字对
【题目描述】
小 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.
思路
可以想到所有特殊区间必定满足区间最小值等于区间GCD。于是可以使用ST表来维护这两个东西,最后二分求解。
目测0(nlogn)的复杂度,我觉得海星。
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 5 using namespace std; 6 7 #define MAXN 500005 8 9 int n; 10 int a[MAXN], ans[MAXN], _ans, g[MAXN][25]; 11 int minn[MAXN][25]; 12 13 int gcd(int a, int b) { 14 return b == 0 ? a : gcd(b, a % b); 15 } 16 17 bool query(int l, int r) { 18 int s = log2(r - l + 1); 19 return min(minn[l][s], minn[r - (1 << s) + 1][s]) == gcd(g[l][s], g[r - (1 << s) + 1][s]); 20 } 21 22 bool judge(int lim) { 23 int tmp = _ans; 24 _ans = 0; 25 for(int i = 1; i <= n - lim; ++i) 26 if(query(i, i + lim)) 27 ans[++_ans] = i; 28 if(_ans == 0) 29 return _ans = tmp, false; 30 return true; 31 } 32 33 int main() { 34 freopen("pair.in", "r", stdin); 35 freopen("pair.out", "w", stdout); 36 37 scanf("%d", &n); 38 39 for(int i = 1; i <= n; ++i) { 40 scanf("%d", &a[i]); 41 minn[i][0] = g[i][0] = a[i]; 42 } 43 44 for(int j = 1; j <= 20; ++j) 45 for(int i = 1; i + (1 << j) - 1 <= n; ++i) 46 minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]), 47 g[i][j] = gcd(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]); 48 49 int l = 0, r = n, maxL; 50 while(l <= r) { 51 int mid = (l + r) >> 1; 52 if(judge(mid)) 53 l = mid + 1, maxL = mid; 54 else 55 r = mid - 1; 56 } 57 58 printf("%d %d\n", _ans, maxL); 59 for(int i = 1; i <= _ans; ++i) 60 printf("%d ", ans[i]); 61 printf("\n"); 62 63 fclose(stdin); 64 fclose(stdout); 65 66 return 0; 67 }