CodeForces-1016C Vasya And The Mushrooms(模拟+思维+前缀和的前缀和) 解题报告 Apare_xzc

CodeForces-1016C Vasya And The Mushrooms(模拟+思维+二重前缀和 ) 解题报告

xzc 2019/4/7


这周周赛的C题:wyt学姐的恶意
  这道题周赛的时候就出思路了,但是觉得可能很不好写,20分钟写了个大概,处理了前缀后缀和以及二重前缀后缀和,没推完公式


vjudge链接
codeforces链接

题意:

  有一个蘑菇园,是一个2*n的方格地。每个格子里都有一颗蘑菇,蘑菇的生长速度不同。
  Vasya这个人一开始站在左上角那个格子里,他必须走到每个格子里去采蘑菇,并且不能停留,1秒移动到一个相邻的格子,并且迅速采了这个格子的蘑菇(时间忽略不计),然后马上离开。而且他必须经过每个格子1次
  一开始时间为蘑菇高度都为0。求这个人能采到蘑菇高度之和的最大值是多少。(左上角那株肯定是0)


输入:

  n
  a1 a2 … an(代表第一行每列蘑菇每秒生长的高度)
  b1 b2 … bn(代表第二行每列蘑菇每秒生长的高度)


输出:

  一个整数,表示他能采到蘑菇高度之歌的最大值


Sample


 input1

3
1 2 3
6 5 4

 output1

70

 input2

3
1 1000 10000
10 100 100000

 output2

543210

input3(不用谢我)

6
12 8 12 17 20 5
17 4 8 8 8 4

output3

705

input4(不用谢我)

6
16 20 18 7 14 2
17 12 4 10 7 4

output4

741

分析:

(以样例4为例)

列数 1 2 3 4 5 6
a(生长速度) 16 20 18 7 14 2
b(生长速度) 17 12 4 10 7 4

我们可以把每个位置用一个字母标号:

列数 1 2 3 4 5 6
a A B C D E F
b G H I J K L
  • 那么,根据题意,这个人一开始在A这里,他必须采了高度为0的蘑菇,然后马上向相邻的格子转移,他只能向下走或者向右走。
  • 那么,根据题目的要求,他每个格子只能走一次,如果他第一步向右边走到了B,那么他的路径就确定了,只能是A->B->C->D->E->F->L->K->J->I->H->G
  • 如果他第一步往下走,到了G,那么他下一步只能往右从G到H
  • 现在他到了H,他又有2种选择:向右走到I(A->G->H->I),或者向上走到B(A->G->H->B)
  • 如果他选的是从H到I,那么他的路径又确定了,只能是A->G->H->I->J->K->L->F->E->D->C->B
  • 如果他选的是从H到B,那么他下一步只能往右从B到C
  • 现在他到了C,他又有两种选择:向右走到D(A->G->H->B->C->D),或者向下走到I(A->G->H->B->C->I)
  • 如果他选的是从C到D,那么他他的路径叒确定了,只能是A->G->H->B->C->D->E->F->L->K->J->I
  • 如果他选的是从C到I,那么下一步他只能往右到J,继续走蛇形…
  • (你懂我意思了吧)

所以,我们的思路就确定了,他能走的路线不多,少于2*n条,所以,我们可以用每条路线的答案ans去更新最终的结果res
我们可以这样写:
  循环的主线是让他走蛇形A->G->H->B->C->I->J->D->E->K->L->F
  在走这条主线的时候,在每个位置,如果该上下了他却往右走了,那么他的路径就确定了,我们计算一下他开小差走的其它路径的答案,更新一下结果,然后继续走蛇形
  那么问题来了,循环主线复杂度是O(2*n)的,我们能否O(1)或者O(logn)地求出图中每个点“开小差”后的结果呢?
我们就想到了前缀和


我们来模拟一下某一条路:
假如我们走蛇形,走到第4列J的位置,此时的时间t=7,我们本应该向上走到D,但是我们从J向右走到了K,那么后面的路就是(A->G->H->B->C->I->J)->K->L->F->E->D
那么我们如何计算从K到D这一段蘑菇的高度(设为h)呢?
我们可以暴力地算一遍:
h = 8*K + 9*L + 10*F + 11*E + 12*D
那么,我们可以先从J和L开始计时,把h表示为:
h = (1+7)*K + (2+7)*L + (1+9)*E + (2+9)*D

那么,7和9是怎么来的呢?
设J所在列数为i,则i = 4
7 = t
10 = 7+6-4 = t+n-i
那么h就可以表示为:

  t * (b[i]+b[i+1]+...+b[n])  -------------------->(1)
+ b[i]*1 + b[i+1]*2 + ... + b[n]*(n-i+1) --------->(2)    
+ (t+n-i) * (a[n]+a[n-1]+a[n-1]+...+a[i])--------->(3) 
+ a[n]*1 + a[n-1]*2 + ... + a[i]*(n+1-i) --------->(4)

那我们要如何求这4个式子呢?

  • 显然(1)(3)可以用数组a,b的前缀和(后缀和更好)
  • 那么(2)(4)怎么求呢?
  • 注意了!(2)不就是数组b<mark>后缀和的后缀和</mark>吗?
        (4)不就是数组a<mark>前缀和的前缀和</mark>吗?

我们可以预处理之后O(1)算出来(1)(2)(3)(4)

至此,这题可以做了,而且复杂度为O(n)
我们先定义几个数组
sumaL: a数组的前缀和
sumaR: a数组的后缀和
sal : a数组前缀和的前缀和
sar : a输入后缀和的后缀和
(b的同理)


上代码:

/* Author: XuZhichao Status: Accepted Time: 124ms Memory: 23512kB Length: 1864 Lang: GNU G++11 5.1.0 Submitted: 2019-04-07 00:53:49 */
#include <bits/stdc++.h>
#define Mst(a,b) memset(a,(b),sizeof(a))
#define For(i,a,b) for(register int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(register int i=(a);i>=(b);--i)
#define LL long long
#define MP make_pair
#define pb push_back
using namespace std;
const int maxn = 3e5 + 100;
LL a[maxn],b[maxn],res; 
LL sumaL[maxn],sumaR[maxn],sumbL[maxn],sumbR[maxn];
LL sal[maxn],sar[maxn],sbl[maxn],sbr[maxn];
void downToRight(LL ans,int i,int n,LL t)
{
	ans += t*sumbR[i+1]+sbr[i+1]+(t+n-i)*sumaR[i]-sal[i-1]+sal[n]-sumaL[i-1]*(n-i+1);
	res = max(res,ans);
}
void upToRight(LL ans,int i,int n,LL t)
{
	ans += t*sumaR[i+1]+sar[i+1]+(t+n-i)*sumbR[i]-sbl[i-1]+sbl[n]-sumbL[i-1]*(n-i+1);
	res = max(res,ans);
}
int main()
{
	//freopen("in.txt","r",stdin);
	int n;
	int ca = 0;
	while(scanf("%d",&n)!=EOF)
	{
		if(ca++)
		{
			sumaL[0] = sumaL[n+1] = 0;
			sumbL[0] = sumbL[n+1] = 0;
			sumaR[0] = sumaR[n+1] = 0;
			sumbL[0] = sumbR[n+1] = 0;
			sal[0] = sal[n+1] = 0;
			sar[0] = sar[n+1] = 0;
			sbl[0] = sbl[n+1] = 0;
			sbr[0] = sbr[n+1] = 0;
		}
		For(i,1,n) scanf("%lld",a+i),sumaL[i] = sumaL[i-1]+a[i];
		For(i,1,n) scanf("%lld",b+i),sumbL[i] = sumbL[i-1]+b[i];
		if(n==1)
		{
			printf("%lld\n",b[1]);
			continue;
		}
		Rep(i,n,1) sumaR[i] = sumaR[i+1]+a[i],sumbR[i] = sumbR[i+1]+b[i];
		For(i,1,n) sal[i]  = sal[i-1]+sumaL[i], sbl[i] = sbl[i-1]+sumbL[i];
		Rep(i,n,0) sar[i]  = sar[i+1]+sumaR[i], sbr[i] = sbr[i+1]+sumbR[i];
		res = 0;
		res = sar[2]+sbl[n]+sumbL[n]*(n-1);
		LL t = 0;
		LL ans = 0;
		For(i,1,n)  
		{
			++t;
			if(i&1) ans += t*b[i];
			else ans += t*a[i];
			if(i==n) 
			{
				res = max(res,ans);
				continue;
			}
			++t;
			if(i&1)
			{
				ans += t*b[i+1];
				if(i<n-1)
					downToRight(ans,i+1,n,t); 
			}
			else
			{
				ans += t*a[i+1];
				if(i<n-1)
					upToRight(ans,i+1,n,t);
			}
		}
		printf("%lld\n",res);
	}
	
	return 0;
}

全部评论

相关推荐

02-25 09:55
已编辑
门头沟学院 Java
2.4&nbsp;一面2.6&nbsp;二面2.9&nbsp;三面(hr面)2.13&nbsp;oc1.15号收到面试电话那会就开始准备,因为一开始没底所以选择推迟一段时间面试,之后开始准备八股,准备实习可能会问的东西,这期间hot100过了有六七遍,真的是做吐了快,八股也是背了忘,忘了背,面经也看了很多,虽然最后用上的只有几道题,可是谁知道会问什么呢自从大二上开始学java以来,一开始做外卖,点评,学微服务,大二下五六月时,开始投简历,哎,投了一千份了无音讯,开始怀疑自己(虽然能力确实很一般),后来去到一家小小厂,但是并不能学到什么东西,而且很多东西都很不规范,没待多久便离开,大二暑假基本上摆烂很怀疑自己,大三上因为某些原因开始继续学,期间也受到一俩个中小厂的offer,不过学校不知道为啥又不允许中小厂实习只允许大厂加上待遇不太好所以也没去,感觉自己后端能力很一般,于是便打算转战测开,学习了一些比较简单的测试理论(没有很深入的学),然后十二月又开始继续投,java和测开都投,不过好像并没有几个面试,有点打击不过并没有放弃心里还是想争一口气,一月初因为学校事比较多加上考试便有几天没有继续投,10号放假后便继续,想着放假应该很多人辞职可能机会大一点,直到接到字节的面试,心里挺激动的,总算有大厂面试了,虽然很开心,但同时压力也很大,心里真的很想很想很想进,一面前几天晚上都睡不好觉,基本上都是二三点睡六七点醒了,一面三十几分钟结束,问的都不太难,而且面试官人挺好但是有些问题问的很刁钻问到了测试的一些思想并不是理论,我不太了解这方面,但是也会给我讲一讲他的理解,但是面完很伤心觉得自己要挂了。但是幸运的是一面过了(感谢面试官),两天后二面,问的同样不算难,手撕也比较简单,但也有一两个没答出来,面试官人很好并没有追问,因为是周五进行的二面,没有立即出结果,等到周一才通知到过了,很煎熬的两天,根本睡不好,好在下周一终于通知二面过了(感谢面试官),然后约第二天三面,听别的字节同学说hr面基本上是谈薪资了,但是我的并不是,hr还问了业务相关的问题,不过问的比较浅,hr还问我好像比较紧张,而且hr明确说了还要比较一下,我说我有几家的面试都拒了就在等字节的面试,三面完后就开始等结果,这几天干啥都没什么劲,等的好煎熬,终于13号下午接到了电话通知oc了,正式邮件也同时发了,接到以后真的不敢信,很激动但更重要的是可以松一口气了,可以安心的休息一下了终于可以带着个好消息过年了,找实习也可以稍微告一段落了,虽然本人很菜,但是感谢字节收留,成为忠诚的节孝子了因为问的比较简单,面经就挑几个记得的写一下一面:1.实习项目的难点说一下2.实习中用到了哪些测试方法3.针对抖音评论设计一下测试用例4.手撕:合并两个有序数组二面:1.为什么转测开2.线程进程区别,什么场景适合用哪个3.发送一个朋友圈,从发出到别人看到,从数据流转的角度说一下会经历哪些过程4.针对抖音刷到广告视频设计测试用例5.手撕:无重复字符的最长字串
厂办龚彪:锲而不舍 金石可镂
查看8道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务