zoukankan      html  css  js  c++  java
  • 【洛谷6631】[ZJOI2020] 序列(思维题)

    点此看题面

    大致题意: 给定一个序列,每次操作你可以选择一段区间,然后将其中所有数/所有下标为奇数的数/所有下标为偶数的数都减(1),求最少操作多少次能够让全部数都变成(0)

    前言

    一道很妙的题目,看完题解感觉很简单,但比赛的时候真的是只会打暴力。。。

    从第一个位置开始考虑

    令将区间内所有数减(1)的操作为第一类操作,将区间内所有下标为奇数/偶数的数减(1)的操作为第二类操作。

    对于第一个位置,处理所有以它为区间左端点的操作,显然我们需要将它减成(0)

    我们分三步贪心:尽可能进行第一类操作(进行(min{a_1,a_2})次),尽可能进行第二类操作(进行(min{a_1,a_3})次),将第一个数减至(0)(进行(a_1)次)。

    贪心的正确性在于,无论何时开始一种新的操作都要付出(1)的代价,而优先进行第一类操作肯定不会使答案变劣。

    扩展到全局

    考虑把(a_1)变成(0)之后,紧接着就可以去按类似的步骤处理(a_2),然后是(a_3,a_4,...,a_n)

    但问题在于当前处理到的位置可能先前已经进行过某些操作了。

    (cur,now)分别表示当前位置进行过的第一类操作和第二类操作数。(这里我们枚举的是(a_2)

    首先,自然要将(cur)(now)分别向(a_i)(min)

    然后,如果(cur+now<a_i),显然可以直接将(a_i)减去(cur+now)

    否则,我们令(k=cur+now-a_i),显然(cur-k)次第一类操作和(now-k)次第二类操作都是必然要执行的,而剩余的(k)次操作可以免费任选第一类操作或是第二类操作。

    为了解决这样的问题,我们可以将答案先减去(k),然后再操作完之后把(a_i)修改回(k),表示免费任选。

    具体实现详见代码。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    using namespace std;
    int n,a[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    }F;
    int main()
    {
    	RI Tt,i,t,k,cur,now,nxt;long long ans;F.read(Tt);W(Tt--)
    	{
    		for(F.read(n),i=1;i<=n;++i) F.read(a[i]);
    		for(cur=now=nxt=ans=0,i=2;i<=n;++i,swap(now,nxt))//每次交换now和nxt
    			cur>a[i]&&(cur=a[i]),now>a[i]&&(now=a[i]),//向a[i]取min
    			cur+now>a[i]?(k=cur+now-a[i],cur-=k,now-=k,a[i]=0):(k=0,a[i]-=cur+now),//若cur+now>a[i]可以免费任选k次操作,否则直接减去
    			ans+=(t=min(a[i-1],a[i])),a[i-1]-=t,a[i]-=t,cur+=t,ans+=a[i-1],nxt+=a[i-1],//优先进行第一类操作,然后是第二类操作
    			k&&(ans-=k,a[i]=k);//免费任选k次操作的处理
    		printf("%lld
    ",ans+a[n]);//注意最后答案加上a[n]
    	}return 0;
    }
    
  • 相关阅读:
    微信公众号项目部署
    数据库存入年月日时分秒类型时间问题
    路径问题
    常用DOS命令
    解决Maven下载慢的问题
    害人不浅的数学翻译
    Webpack4 踩坑记
    React 踩坑记
    what's the problem of Object oriented programming
    为什么JVM调优?
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6631.html
Copyright © 2011-2022 走看看