Uva 1609 Feel Good

题面:给出长度为n的数列,然后算出其区间和乘区间最小数所能得到的最大值,并且输出区间

样例输入:

6

3 1 6 4 5 2

样例输出:

60

3 5

原题链接:https://vjudge.net/problem/UVA-1619



分析:

这里有两种算法,一种是O(nlogn)的,用st表+递归,另一种是O(n)的,用单调栈。

容易知道对于数列中的每一个数作为相应区间最小值时,虽然这个相应区间不一定唯一的,但是这个最大区间和一定是唯一的。

举个栗子:

对于数列{0, 0, 0, 0, 0}来说,我们不管是选择哪一个元素作为区间最小值,其相应的答案区间都有多种可能,但最大区间和肯定都是0(你选取第一个0作为区间最小值, 那区间可以是[1, 1],[1, 2],[1, 3]等等,但是区间和最大值肯定都是0)。

我们看原题的话就会发现原题说明是关于这个区间的输出,我们输出其中合法的任何一个就行了。

但是!但是!其实这是在坑你!表面上是spj,实际上并不是spj!对于每一个给定的数据,其输出的区间也必须也是要跟他给的一毛一样才行orz,具体的潜规则我也不是很清楚,反正用单调栈才能AC,不过本着真理至上的原则,还是两个算法都讲一遍。

================

st表+递归解法

这种做法是枚举区间,然后通过st表O(1)找出区间的最小值,配合前辍和能直接计算出一个可能答案。

那怎么枚举区间呢?

先说下,st表保存的应该是区间最小值的下标 。

比较直观的一点就是当我们选取这个数列的最小值作为区间的最小值的时候,这个区间[1, n]肯定是合法的,设这个最小值的位置为k,那么我们可以知道[1, k-1], [k+1, n]这两个区间是另外两个该被枚举的区间,然后找出这两个区间最小值的位置,又能将区间再次分割。。。每次所划分的区间所对应的区间最小值所在的位置都是不一样的,通过这个递归过程,如此一来,我们就得到了一个O(n)枚举区间的方法,之所以这个算法是O(nlogn)只是因为建st表要O(nlogn)罢了,其他操作要么O(n)要么O(1)的,只可惜实际上这道题不是spj,这种方法跟单调栈的做法虽然都是对的,但是所得区间不一定一样,不然数据水点的话说不定也能过(x)。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5
 6 const int N = 1e5 + 10;
 7 int Logn[N];
 8 long long a[N];
 9 long long sum[N]; //前辍和
10 int st[N][30]; //记录区间[l, r]中最小元素下标的st表
11 long long ans; //最终答案
12 int L, R; //最终答案区间
13
14 void pre() {
15     Logn[1] = 0;
16     for (int i = 2; i < N; i++)
17         Logn[i] = Logn[i / 2] + 1;
18 }
19
20 void Build_st(int n) //建立st表
21 {
22     int logn = Logn[n];
23     for(int j=1; j<=logn ;j++)
24         for (int i = 1; i + (1 << j) - 1 <= n; i++) {
25             if (a[st[i][j - 1]] < a[st[i + (1 << (j - 1))][j - 1]])
26                 st[i][j] = st[i][j - 1];
27             else st[i][j] = st[i + (1 << (j - 1))][j - 1];
28         }
29 }
30
31 int Query(int l, int r)
32 {
33     int s = Logn[r - l + 1];
34
35     if (a[st[l][s]] < a[st[r + 1 - (1 << s)][s]]) return st[l][s];
36     else return st[r + 1 - (1 << s)][s];
37 }
38
39 void solve(int l, int r) //递归处理
40 {
41     if (l > r) return;
42
43     int m = Query(l, r);
44     long long res = (sum[r] - sum[l-1]) * a[m];
45     if (res > ans) {
46         ans = res;
47         L = l;
48         R = r;
49     }
50
51     solve(l, m - 1);
52     solve(m + 1, r);
53 }
54
55 int main()
56 {
57     //freopen("data.txt", "r", stdin);
58     //freopen("WA.txt", "w", stdout);
59     ios::sync_with_stdio(false);
60     pre();
61     int n;
62     while (cin >> n) {
63         ans = -1;
64
65         for (int i = 1; i <= n; i++) {
66             cin >> a[i];
67             sum[i] = a[i] + sum[i - 1];
68             st[i][0] = i;
69         }
70
71         Build_st(n);
72         solve(1, n);
73         cout << ans << endl;
74         cout << L << " " << R << endl;
75         cout << endl;
76     }
77
78     return 0;
79 }

================

单调栈解法

单调栈的话做法就很单纯了,用数组l[i], r[i]分别保存元素a[i]作为区间最小值时所能延伸到的最左端跟最右端,用单调栈扫描两次这两个数组的值就能全部出来了,实际写代码的时候博主写惯了不小心写成了单调队列,不过操作都一样的。。。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<queue>
 4 using namespace std;
 5
 6 typedef long long ll;
 7 const int Maxn = 1e5 + 10;
 8 ll a[Maxn];
 9 ll l[Maxn];
10 ll r[Maxn];
11 ll sum[Maxn];
12
13 int main()
14 {
15     int n;
16     bool first = true;
17     while (cin >> n) {
18         if (first) first = false;
19         else cout << endl;
20
21         for (int i = 1; i <= n; i++) {
22             cin >> a[i];
23             sum[i] = sum[i - 1] + a[i];
24         }
25
26         deque <ll > rq, lq;
27         for (int i = 1; i <= n; i++) {
28             while (!rq.empty() && a[rq.back()] > a[i]) {
29                 r[rq.back()] = i - 1;
30                 rq.pop_back();
31             }
32
33             rq.push_back(i);
34         }
35         while (!rq.empty()) {
36             r[rq.front()] = n;
37             rq.pop_front();
38         }
39
40         for (int i = n; i >= 1; i--) {
41             while (!lq.empty() && a[lq.back()] > a[i]) {
42                 l[lq.back()] = i + 1;
43                 lq.pop_back();
44             }
45
46             lq.push_back(i);
47         }
48         while (!lq.empty()) {
49             l[lq.front()] = 1;
50             lq.pop_front();
51         }
52
53         ll ans = 0;
54         ll ans_l = 1, ans_r = 1;
55
56         for(int i=1; i<=n ;i++)
57             if ((sum[r[i]] - sum[l[i] - 1]) * a[i] > ans) {
58                 ans = (sum[r[i]] - sum[l[i] - 1]) * a[i];
59                 ans_l = l[i];
60                 ans_r = r[i];
61             }
62
63         cout << ans << endl;
64         cout << ans_l << " " << ans_r << endl;
65     }
66
67     return 0;
68 }

原文地址:https://www.cnblogs.com/wulichenai/p/12249433.html

时间: 2024-10-11 06:39:10

Uva 1609 Feel Good的相关文章

UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)

题意:有n支队伍(n是2的整数幂,2<=n<=4),打淘汰赛,胜者进入下一轮,其中1号队伍能打败至少一半的队伍,对于它不能打败的队伍l,一定存在一支它能够打败的队伍w,使得w能直接打败l,求一种方案保证1号队伍必胜. 队伍分类:不能直接打败的队伍是黑色队伍,能直接打败且能打败黑色队伍的是灰色队伍. 这里直接给出算法描述了: 阶段1:贪心,每个不能直接打败的队伍,选一个没有匹配的能打败它的灰色队伍. 阶段2:对于剩下的黑色队伍,任意匹配,可能会剩下一个. 阶段3:找一个能直接打败的队伍和1号匹配

UVa 1609 (博弈) Foul Play

姑且把它归类为一道博弈吧,毕竟这也是在找必胜方案. 十分有意思的一道题目,设计一种方案让你支持的1队获胜. 题目给出了两个很重要的条件: 1队能打败至少一半的队伍 对于1队不能打败的黑队,一定存在一个1队能打败的灰队,使得这支灰队能够打败黑队.也就是说1队可以通过灰队间接打败黑队 一共有2n支队伍,每轮比赛会刷掉一半的队伍,紫书上巧妙的做法就是每轮比赛后让题目给的两个性质依然成立,这样1队最终一定能胜出. 方案如下,大致分为3个阶段: 物尽其用.依次考虑每个黑队,如果有能够打败他的灰队的话,便让

UVA 562 Dividing coins --01背包的变形

01背包的变形. 先算出硬币面值的总和,然后此题变成求背包容量为V=sum/2时,能装的最多的硬币,然后将剩余的面值和它相减取一个绝对值就是最小的差值. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define N 50007 int c[102],d

UVA 10341 Solve It

Problem F Solve It Input: standard input Output: standard output Time Limit: 1 second Memory Limit: 32 MB Solve the equation: p*e-x + q*sin(x) + r*cos(x) + s*tan(x) + t*x2 + u = 0 where 0 <= x <= 1. Input Input consists of multiple test cases and te

UVA 11014 - Make a Crystal(容斥原理)

UVA 11014 - Make a Crystal 题目链接 题意:给定一个NxNxN的正方体,求出最多能选几个整数点.使得随意两点PQ不会使PQO共线. 思路:利用容斥原理,设f(k)为点(x, y, z)三点都为k的倍数的点的个数(要扣掉一个原点O).那么全部点就是f(1),之后要去除掉共线的,就是扣掉f(2), f(3), f(5)..f(n).n为素数.由于这些素数中包括了合数的情况,而且这些点必定与f(1)除去这些点以外的点共线,所以扣掉.可是扣掉后会扣掉一些反复的.比方f(6)在f

[UVa] Palindromes(401)

UVA - 401 Palindromes Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description A regular palindrome is a string of numbers or letters that is the same forward as backward. For example, the string "ABCDED

uva 401.Palindromes

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=342 题目意思:给出一段字符串(大写字母+数字组成).判断是否为回文串 or 镜像串 or 回文镜像串 or 什么都不是.每个字母的镜像表格如下 Character Reverse Character Reverse Character Reverse A A M M Y Y B

[2016-02-19][UVA][129][Krypton Factor]

UVA - 129 Krypton Factor Time Limit: 3000MS Memory Limit: Unknown 64bit IO Format: %lld & %llu Submit Status Description You have been employed by the organisers of a Super Krypton Factor Contest in which contestants have very high mental and physica

[2016-02-03][UVA][514][Rails]

时间:2016-02-03 22:24:52 星期三 题目编号:UVA 514 题目大意:给定若干的火车(编号1-n),按1-n的顺序进入车站, 给出火车出站的顺序,问是否有可能存在 分析:    FIFO,用栈模拟一遍即可, 方法:    根据输入的顺序,从1-n开始,当前操作的为i 如果i是当前对应的编号,那么直接跳过(进入B) 如果不是,根据当前需求的编号,小于i,就从栈顶弹出一个元素, 看这个元素是否是需求的,是则继续.否则NO 1 2 3 4 5 6 7 8 9 10 11 12 13