看了刘汝佳的 翻译 我也是醉了。 原题明明数据是 0- 100000 书上翻译给了个 正整数。。
自己这个题很久之前就看了。 想了很久没想出来。 今天在看线段树。 看到了 RMQ, 就是求区间最小值得。 然后自己就回头又看这个题。
数据量太大了。 用 RMQ 是没法做出来的。 然后自己只能去网上找了题解,
对于每一个数据 v【i】 都可以找到 它是某个区间的 最小值, 左短点是 l【】 右短点是 r【】
提前预处理 sum【i】 前i项和 那么 区间的 结果就好求了。 关键的地方就是 在 确定 l 和 r 的问题上
别人的思路确实很好。 比如 一组数据 6 5 4 3 2 3的左端点 求出来了 是 1 那么在 求 2 的左端点时 因为 3 比 2大 那么 3的左端点一定是 2的左端点
这样直接赋值给2 然后 再去求3的左端点就好了。 相比以前的 挨个遍历要快很多。 同理 右端点一样处理
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <cctype>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define maxn 1000010
#define INF 1<<30
ll sum[maxn];
int v[maxn],l[maxn],r[maxn];
int main (){
int n;
int kase = 0;
while(scanf("%d",&n) != EOF){
sum[0] = 0;
if(kase)
printf("\n");
kase++;
for(int i = 1; i <= n; i++){
scanf("%lld",&v[i]);
sum[i] = sum[i-1] + v[i];
l[i] = i;
r[i] = i;
}
for(int i = 1; i <= n; i++){
if(v[i] > 0)
while(v[l[i]-1] >= v[i]){
l[i] = l[l[i]-1];
}
}
for(int i = n; i >= 1; i--){
if(v[i] > 0)
while(v[r[i] + 1] >= v[i]){
r[i] = r[r[i] + 1];
}
}
ll ans = 0;
int ll = 1, rr = 1;
long long nu;
for(int i = 1; i <= n; i++){
if(v[i] > 0){
nu = v[i] *(sum[r[i]] - sum[l[i] - 1]);
if(nu > ans){
ans = nu;
ll = l[i];
rr = r[i];
}
else if(nu == ans){
if(rr - ll == r[i] - l[i] && l[i] < ll)
ll = l[i],rr = r[i];
}
}
}
printf("%lld\n%d %d\n",ans,ll,rr);
}
return 0;
}
再说一下 RMQ
RMQ 就比较简单了。 在一个区间内。 把它分成两部分。 分别取这两部分的最小值就好了。
它是用二维数组 d【i】【j】 来表示的 从 i 到 j 的最小值。 那么 像上面那样 100000 最大的数据量 肯定是 开不开的。 只能用上面的方法吧
具体代码实现 训练之南 P198