COJ 2004 序列

传送门: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\n", 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\n", _max);
63     return ;
64 }
65 int main(){
66     input();
67     work();
68     output();
69     return 0;
70 }

排序o(nlogn),扫描o(n),感觉很不错啊。

posted @ 2015-03-29 22:40  AI_Believer  阅读(209)  评论(0编辑  收藏  举报