zoukankan      html  css  js  c++  java
  • 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后缀和的后缀和吗?
          (4)不就是数组a前缀和的前缀和吗?

    我们可以预处理之后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
    ",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
    ",res);
    	}
    	
    	return 0;
    }
    

  • 相关阅读:
    说说Cookie和Session
    说说SQL Server的数据类型
    Java访问权限控制
    数据库-- 触发器
    关于jni编译32位、64位动态库(Android.mk和Application.mk文件)
    Android中的 init.rc文件简介
    Android系统属性简介
    Android Support 包知识
    Android窗口机制分析与UI管理系统
    Android界面架构(Activity,PhoneWiondow,DecorView)简介
  • 原文地址:https://www.cnblogs.com/Apare-xzc/p/12243655.html
Copyright © 2011-2022 走看看