传送门:http://oj.cnuschool.org.cn/oj/home/addSolution.htm?problemID=978
试题描述:
WZJ的数字游戏又开始了。他写了N个自然数Ai到黑板上,让你选取一个起点L和一个终点R使sum(L,R)*min(L,R)最大,请你输出这个最大值。
sum(L,R)表示AL一直加到AR之和,min(L,R)表示AL到AR的最小值。
输入:
第一行为一个正整数N。
接下来为N个自然数Ai。
输出:
输出最大值。
输入示例:
7
1 5 2 0 5 5 7
输出示例:
85
其他说明:
1<=N<=100000
0<=Ai<=1000000
题解:
思路1:枚举所有的最小值,用单调栈或二分RMQ维护一下往前到哪里往后到哪里,然后乱搞答案。
单调栈32ms最快:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 6 inline void read(int& x) 7 { 8 x = 0; 9 int sig = 1; 10 char ch = getchar(); 11 while(!isdigit(ch)) 12 { 13 if(ch == '-') sig = -1; 14 ch = getchar(); 15 } 16 while(isdigit(ch)) 17 { 18 x = x * 10 + ch - '0'; 19 ch = getchar(); 20 } 21 x *= sig; 22 return ; 23 } 24 25 const int maxn = 100000 + 10; 26 27 int A[maxn]; 28 int L[maxn], R[maxn]; 29 30 int S[maxn]; 31 32 long long v[maxn], ans; 33 34 int main() 35 { 36 int n; 37 read(n); 38 39 int top = 0; 40 41 for(int i = 1; i <= n; i++) 42 { 43 read(A[i]); 44 v[i] = v[i - 1] + A[i]; 45 } 46 47 A[0] = A[n + 1] = -1; 48 S[++top] = 0; 49 50 for(int i = 1; i <= n; i++) 51 { 52 while(A[S[top]] >= A[i]) top--; 53 L[i] = S[top] + 1; 54 S[++top] = i; 55 } 56 57 S[top = 1] = n + 1; 58 59 for(int i = n;i ; i--) 60 { 61 while(A[S[top]] >= A[i]) top--; 62 R[i] = S[top] - 1; 63 S[++top] = i; 64 65 ans = max(ans, A[i] * (v[R[i]] - v[L[i] - 1])); 66 } 67 68 printf("%lld", ans); 69 70 return 0; 71 }
由于每一个元素只进入单调栈一次,所以是o(n)的。
RMQ157ms最慢:
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 5 const int maxn = 100000 + 10; 6 7 int A[maxn]; 8 9 int n; 10 11 int log[maxn], d[maxn][20]; 12 13 long long ans, S[maxn]; 14 15 int f1[maxn], f2[maxn]; 16 17 void RMQ_init() 18 { 19 log[0] = -1; //你大爷 20 for(int i = 1; i <= n; i++) 21 { 22 d[i][0] = A[i]; 23 log[i] = log[i >> 1] + 1; 24 } 25 26 for(int j = 1; (1 << j) <= n; j++) 27 for(int i = 1; i + (1 << j) - 1 <= n; i++) //你大爷 28 d[i][j] = min(d[i][j - 1], d[i + (1 << (j - 1))][j - 1]); 29 30 return ; 31 } 32 33 int query(int L, int R) 34 { 35 int k = log[R - L + 1]; 36 return min(d[L][k], d[R - (1 << k) + 1][k]); 37 } 38 39 int main() 40 { 41 int m, L, R, M; 42 scanf("%d", &n); 43 44 for(int i = 1; i <= n; i++) 45 { 46 scanf("%d", &A[i]); 47 S[i] = S[i - 1] + A[i]; 48 } 49 50 RMQ_init(); 51 52 for(int i = 1; i <= n; i++) 53 { 54 L = 1; 55 R = i; 56 57 while(L < R) 58 { 59 M = L + R >> 1; 60 if(query(M, i) == A[i]) R = M; 61 else L = M + 1; 62 } 63 64 f1[i] = L; 65 } 66 67 for(int i = 1; i <= n; i++) 68 { 69 L = i; 70 R = n + 1; 71 72 while(L + 1 < R) 73 { 74 M = L + R >> 1; 75 if(query(i, M) == A[i]) L = M; 76 else R = M; 77 } 78 79 f2[i] = L; 80 } 81 82 for(int i = 1; i <= n; i++) 83 ans = max(ans, (S[f2[i]] - S[f1[i] - 1]) * A[i]); 84 85 printf("%lld ", ans); 86 87 return 0; 88 }
RMQ初始化o(nlogn),查询o(nlogn)。
思路2:将A[i]从大到小排,用并查集维护每个元素的集合,每次将该元素左右的不小于该元素本身的元素合并进来,更新答案。
正确性有待考察……以后补……
并查集47ms还可以:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 100000 + 10; 7 int f[maxn], Min[maxn]; 8 long long Sum[maxn], _max = 0; 9 int n, P[maxn]; 10 11 struct Node{ 12 int v, id; 13 bool operator < (const Node& ths) const{ 14 return v < ths.v; 15 } 16 }A[maxn]; 17 18 void read(int& x){ 19 x = 0; 20 int sig = 1; 21 char ch = getchar(); 22 while(!isdigit(ch)){ 23 if(ch == '-') sig = -1; 24 ch = getchar(); 25 } 26 while(isdigit(ch)){ 27 x = 10 * x + ch - '0'; 28 ch = getchar(); 29 } 30 x *= sig; 31 return ; 32 } 33 void input(){ 34 read(n); 35 for(int i = 1; i <= n; i++) read(P[i]), A[i].id = f[i] = i, A[i].v = Min[i] = Sum[i] = P[i]; 36 return ; 37 } 38 int findset(int x){ 39 return f[x] == x ? x : f[x] = findset(f[x]); 40 } 41 void merge(int d1, int d2){ 42 int f1 = findset(d1); 43 int f2 = findset(d2); 44 if(f1 != f2){ 45 Sum[f1] += Sum[f2]; 46 Min[f1] = min(Min[f1], Min[f2]); 47 f[f2] = f1; 48 _max = max(_max, Sum[f1] * Min[f1]); 49 } 50 return ; 51 } 52 void work(){ 53 sort(A + 1, A + 1 + n); 54 for(int i = n; i; i--){ 55 int x = A[i].id; 56 if(x != 1 && P[x] <= P[x - 1]) merge(x, x - 1); 57 if(x != n && P[x] <= P[x + 1]) merge(x, x + 1); 58 } 59 return ; 60 } 61 void output(){ 62 printf("%lld ", _max); 63 return ; 64 } 65 int main(){ 66 input(); 67 work(); 68 output(); 69 return 0; 70 }
排序o(nlogn),扫描o(n),感觉很不错啊。