【洛谷6631】[ZJOI2020] 序列(思維題)
阿新 • • 發佈:2020-07-16
大致題意: 給定一個序列,每次操作你可以選擇一段區間,然後將其中所有數/所有下標為奇數的數/所有下標為偶數的數都減\(1\),求最少操作多少次能夠讓全部數都變成\(0\)。
前言
一道很妙的題目,看完題解感覺很簡單,但比賽的時候真的是隻會打暴力。。。
從第一個位置開始考慮
令將區間內所有數減\(1\)的操作為第一類操作,將區間內所有下標為奇數/偶數的數減\(1\)的操作為第二類操作。
對於第一個位置,處理所有以它為區間左端點的操作,顯然我們需要將它減成\(0\)。
我們分三步貪心:儘可能進行第一類操作(進行\(\min\{a_1,a_2\}\)次),儘可能進行第二類操作(進行\(\min\{a_1,a_3\}\)
貪心的正確性在於,無論何時開始一種新的操作都要付出\(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\)
否則,我們令\(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\n",ans+a[n]);//注意最後答案加上a[n] }return 0; }