洛谷「Daniel13265 的公开赛」A-替换

洛谷「Daniel13265 的公开赛」A-替换

题目链接:https://www.luogu.com.cn/problem/P6297
时间限制:1.00s | 内存限制:125.00MB

题目背景

替换永远比删除更彻底。

题目描述

Daniel13265 有一串由各种漂亮的贝壳组成的项链,但由于各种原因,这个项链不是环形的,而仅仅是用一根普通的丝线串起来的。项链上的每个贝壳都有一个好看程度 ai,相同种类的贝壳有着相同的好看程度,而不同种类的贝壳有着不同的好看程度。

Danie13265 定义, 第 l 个至第 r 个这一段贝壳是对称的,当且仅当
∑ i = l r ( a i − a l + r − i ) 2 = 0 \sum_{i=l}^r(a_i-a_{l+r-i})^2=0 i=lr(aial+ri)2=0
Daniel13265 经常从中取出一段贝壳。如果这一段贝壳是对称的,他就会非常高兴;如果这一段贝壳不是对称的,那么他会将其中的某些贝壳替换成新的,以使得这一段贝壳成为对称的。一次替换可以任意地改变任何一个位置上贝壳的好看程度,但是过多的替换会使这一段贝壳脱离原本的模样,所以 Daniel13265 至多会进行 k 次替换。如果一段贝壳在进行至多 k 次替换后能够成为对称的,那么 Daniel13265 就称这一段贝壳是「可观赏的」。

Daniel13265 简单地将第 l 个至第 r 个这一「可观赏的」的贝壳段的「观赏指数」定义为
∏ i = l r a i \prod_{i=l}^r a_i i=lrai
其中 ai 表示第 i 个贝壳原本的好看程度
他现在很好奇,在这个贝壳组成的项链中,「可观赏的」贝壳段中「观赏指数」的最大值。但是由于这个值可能很大,所以你只需要求出它对 109+7 取模后的结果即可。

输入格式

输入共 2 行。
第一行两个正整数 n,k,表示这个贝壳组成的项链中贝壳的数目与 Daniel13265 对一段贝壳最多进行替换的次数。
第二行 n 个用单个空格隔开的正整数,第 i 个数 ai表示项链上第 i 个贝壳的好看程度。

输出格式

输出一行一个非负整数,表示「可观赏的」贝壳段中「观赏指数」的最大值对 109+7 取模后的结果。

输入输出样例
输入#1

7 1
1 2 4 2 3 3 4

输出#1

288

输入#2

6 1
3 1 2 250000002 1 2

输出#2

1

说明/提示
样例解释#1

「可观赏的」贝壳段有 [1],[2],[3],[4],[1,2],[2,3],[2,4],[3,3],[3,4],[4,2],[1,2,4],[2,3,3],[2,4,2],[3,3,4],[4,2,3],[2,3,3,4],[4,2,3,3,4],其中「观赏指数」最大的贝壳段为 [4,2,3,3,4]。

样例解释#2

「可观赏的」的贝壳段中「观赏指数」最大的为 [2,250000002,1,2],其值为 109+8,对 109+7 取模后结果为 1。

数据范围

对于 100% 的数据,满足 1 ≤ n ≤ 1000,0 ≤ k ≤ n,1 ≤ ai ≤ 109+7

题意简析

这道题,作为A题,题干着实有点恐怖,简直成了数学小作文阅读理解,刚开始就劝退好多人(包括我)
后来题面看了一轮,又回来了…
咳咳,那么这个题干到底讲了啥呢。In short:(写到这里突然尬住,到底讲啥了呢(挠头)

①有一串由贝壳组成的项链(非环),每个贝壳有一个「好看程度」,ai

②定义,l 至 r 段贝壳是对称的,当且仅当(上面那个和式成立)
我们小学二年级就学过:若平方项的和为0,则每一平方项均为0
即,ai==al+r-i,而 i 从 l 开始递增,也就是说 l~r 段关于中轴对称(物理上的对称)

③一次替换可以改变任意贝壳的「好看程度」,假如一段贝壳经过 k 次替换后,是对称的,
则称 l~r 这一段贝壳为「可观赏的」(有种机翻的赶脚?

④定义一段「可观赏的」贝壳的「观赏指数」为这一段贝壳「好看程度」的连乘积(上式)

⑤求表示「可观赏的」贝壳段中「观赏指数」的最大值对 109+7 取模后的结果

注意:求「观赏指数」时的ai原本的ai,而非替换后的ai
实际上并未真正替换

注意:最大值的取模,而非取模的最大值

假·分析

这题写着好累啊(咋这么多字)
首先,乍一看虽然有点复杂,但好在n不是很大,思路也挺简单:
双重for循环,外层枚举区间长度len,内层枚举区间左端点 l,计算该区间是否能在k次替换内成为对称的
如果能,则计算连乘积,并与最大值做取舍,复杂度O(n2),还是可以承受的。
然后你就会看到一个又大又红的WA (WA地一声哭出来)

真·分析

「醉翁之意不在酒」啊,这出题人是真的黑
那么一大坨题干根本就不是重点,主要问题是这个数据范围(1 ≤ ai ≤ 109+7)(1 ≤ n ≤ 1000)
要命了
这可是连乘积啊,(109+7)1000,打个折109000,这可真是,玉皇大帝来了都没辙
想啥呢,乘起来?桃子
所以作者说要模上109+7,但却是先求最大值再取模,先取模会改变大小关系

也就是说,摆在我们面前的问题是:如何在不求值的情况下,比较两个连乘积的大小
而且最大值无法用特定值表示,改用区间形式(int l, r)

此时,有两种办法:
①取对数
log10(a*b*…*z)==log10(a)+log10(b)+…+log10(z)
这时,109000 便转化为9000,用double比较大小不在话下。
但是,有一个问题,log10()函数计算效率不尽如人意,如果每次直接计算,势必超时(血的教训)
此时我们可以发现,计算存在冗余,可以采用 预处理出对数数组 的方式解决

②两个连乘积中的每一项相除,拆分比较
例:A=a*b*c
B=d*e*f*g*h
t=(a/d)*(b*e)*(c/f)
while(t>=1)t/=g;…t/=h;
if(t>1)A>B;
实质上是比较A/B,做商,拆项可以防止溢出

两种方法都是O(n)地扫描数组,但除法比log更快,所以可以直接计算,无需预处理(这还真不好预处理),开了O2优化后时间大概在700ms左右,卡得死死的。不开就炸了。

当然我觉得第一种更好(200ms),(第二种是比赛时我的暴力美学…先写上再说)
这里采用取对数的方法(赛后补的一发)(更短一些)

"Talk is Cheap. Show me the Code."

#include<stdio.h>
#include<math.h>
typedef long long ll;
const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
int a[maxn];
double Log[maxn];
struct Node {
	int l;
	int r;
	double sum;
};
int main(void)
{
	int n, k;
	scanf("%d %d", &n, &k);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]), Log[i] = log10(a[i]);//预处理,否则超时
	Node Max = { 1,1,Log[1] };
	for (int len = 1; len <= n; len++)
		for (int i = 1; i <= n - len + 1; i++) {
			int l = i, r = l + len - 1;//计算右端点
			int cnt = 0;
			while (l < r) {//计算需替换次数
				if (a[l] != a[r])
					cnt++;
				l++, r--;
			}
			if (cnt <= k) {//若「可观赏的」
				l = i, r = l + len - 1;
				double sum = 0.0;
				for (int j = l; j <= r; j++)
					sum += Log[j];
				if (sum > Max.sum)
					Max = { l,r ,sum };
			}
		}
	ll res = 1;
	for (int i = Max.l; i <= Max.r; i++)
		res *= a[i], res %= mod;
	printf("%lld\n", res % mod);
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值