题解 - 唐克的新游戏

题目描述

唐克新入手了一款PS333平台的游戏,在游戏中从起点到终点共有n个得分点,编号为1~n,需要使用灵能手套才能获得得分点分数,灵能手套经过充能后只可以使用3次。
游戏最终成绩的计算规则如下:假设通过灵能手套获取了三个不同的编号分别为i,j,k的得分点的分数:ai,aj,ak,最终成绩为3个得分点分数总和先减去所选取三点中编号最小值再减去所选取三点中编号最大值,即ai + aj + ak - min(i,j,k) - max(i,j,k)。请你编写程序帮助唐克获得最好成绩。
注:min(i,j,k)表示i,j,k中的最小值,max(i,j,k)表示i,j,k中的最大值。

输入

第一行包含一个整数t,表示有t组测试数据
每组测试数据包含两行:
第一行包含一个整数n,表示游戏中得分点的数量
第二行包含n个整数,表示从起点到终点n个得分点的分数,第i个得分点的分数为ai。

输出

每组测试数据一行,输出唐克所能够获得的最好成绩。

样例

样例输入

【样例1】
1
6
1 2 2 3 3 3
【样例2】
2
6
19 13 4 27 7 11
8
8 7 6 5 4 3 2 1
【样例3】
1
10
1 2 3 9 9 9 10 10 10 10

样例输出

【样例1】
1
【样例2】
54
17
【样例3】
17
提示
对于20%的数据:1<t≤10,3≤n≤100;
对于60%的数据:1≤t≤20,3≤n≤1000
对于100%的数据:1≤t≤100,3≤n≤1e5,1≤ai≤1e8。输入文件的大小不超过10MB.

分析

ai + aj + ak - min(i,j,k) - max(i,j,k) 的最大值
假定 i < j < k ,转化为ai + aj + ak - i - k ,即(ai - i) + aj + (ak - k)

故可以枚举j,

预处理出 b[i] = a[i] - i,通过ST表b[1]~b[j - 1]b[j + 1]~b[n] 的最大值,

ST表
用途:用于解决可重复贡献问题,适用于静态区间,不支持修改操作
条件:对于运算opt,满足 x opt x = x 且 opt 满足结合律
时间复杂度:预处理 O(nlogn) 和 查询 O(1)
经典应用:RMQ,区间GCD

三者的和即为局部最优解,

不断取max即为全局最优解

代码

#include<bits/stdc++.h>
  
using namespace std;
  
typedef pair<int,int> PII;
  
const int N = 1e5 + 10,M = 18 + 10;
 
int n;
int a[N];
int lg[N],f[N][M];
 
void init(){
    lg[1] = 0,lg[2] = 1;
    for(int i = 3;i < N;i++) lg[i] = lg[i >> 1] + 1;
}
 
void st_pre(){
    for(int i = 1;i <= n;i++)
        for(int j = 1;j < M;j++)
            f[i][j] = INT_MIN;
    for(int i = 1;i <= n;i++) f[i][0] = a[i] - i;
 
    for(int j = 1;j < M;j++)
        for(int i = 1;i + (1 << j) - 1 <= n;i++)
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
 
int query(int l,int r){
    int k = lg[r - l + 1];
    return max(f[l][k], f[r - (1 << k) + 1][k]);
}
 
void solve(){
    cin >> n;
    for(int i = 1;i <= n;i++) cin >> a[i];
 
    st_pre();
 
    int res = INT_MIN;
    for(int i = 2;i <= n - 1;i++) res = max(res,a[i] + query(1,i - 1) + query(i + 1,n));
     
    cout << res << '\n';
}
  
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
 
    init();
  
    int t;
    cin >> t;
    while(t--){
        solve();
    }
  
    return 0;
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值