[动态规划]ACM预选赛F题 侠客行

这道题是我觉的这次预选赛中题目最难的一道,比赛过程中我并没有做出来。
这种动态规划的思路太巧秒了,我是想不到的。
再一次膜拜jxy学长

题目链接

描述

众所周知,龙岛主和木岛主发现了侠客岛上的武功秘诀,这武功秘诀是李白《侠客行》的图解,含义极是深奥繁复。他二人修习数月后,忽对图解中所示武功产生了歧见。二人相互辩难剖析,钻研其中道理,然终不能解。于是广邀天下奇才异能之士同来岛上,各竭心思,一起参研。恰好其时岛上的“断肠蚀骨腐心草”开花,此草若再配以其他佐使之药,熬成热粥,服后于练武之士大有裨益。于是派出使者,邀请当世名门大派的掌门人、各教教主、各帮帮主到侠客岛喝碗腊八粥,再请他们去参研图解。现有许多人到了侠客岛,他们决定先进行一番较量,当然了,点到为止。

现有n个人排成一行,编号为1…n,每个人的内力为ai。

对于每两个人(这两个人可以是同一个人),这两个人及其之间的所有人进行一次较量,胜者为他们中内力最大者。求所有比赛中胜者内力和。

如果这场比赛只有一个人的话,他本身就是获胜者。

输入

第一行包含一个整数 T (0<T<=1000)表示有 T 组测试数据

对于每组数据输入格式如下:

第一行为一个整数n (1<n<=100000)表示人的数量。

第二行为n个整数ai,(0<ai<10^9),表示第i个人的内力。

数据保证n的总和不超过10^6。(数据加强了,请用 long long)

输出

输出一个整数,为所有比赛中胜者内力和

输入样例

2
4
1 2 3 4
5
2 3 1 4 2

输出样例

30
49

提示

Sample1

(max(1)+max(2)+max(3)+max(4)) + (max(1,2)+max(2,3)+max(3,4)) + (max(1,2,3)+max(2,3,4)) +(max(1,2,3,4))

=(1+2+3+4) + (2+3+4) + (3+4) +4

Sample2

(2+3+1+4+2) + (3+3+4+4) + (3+4+4) + (4+4) + (4)


题解

这道题目让我们求出在所有任意区间中最大值的和
题目数据量很大只能用 O(n)左右的算法
一下代码是学长的,我复写了一遍 加了几行注释 希望可以帮忙理解

//膜拜出题人大佬 
#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;

const LL maxa = 1e11;
const int maxn = 1e6 + 10;
LL a[maxn];
LL sum[maxn];
int p[maxn];//指向第i个人从左依次数第一个比他能力强的人

int main(){
	int t;
	cin >> t;
	while(t--){
		LL ans = 0;
		int n;
		scanf("%d", &n);
		scanf("%d", &a[1]);
		sum[0] = p[1] = p[0] = 0;
		a[0] = maxa, ans = sum[1] = a[1];
		for(int i = 2; i <= n; i++){
			scanf("%d", &a[i]);
			p[i] = i - 1;
			while(a[p[i]] <= a[i])
				p[i] = p[p[i]];
			sum[i] = sum[p[i]] + a[i]*(i - p[i]);
			//这第i个人从左依次数第一个比他能力强的人 在他左边区间胜的场数和带上第i个人的区间胜的场数一样多 
			//所以只用p[i]指向左边比他大的人,真的妙 
			ans += sum[i];
		}
		cout << ans << "\n";
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值