[JZOJ4788] 【NOIP2016提高A组模拟9.17】序列
題目
描述
題目大意
一個序列,每次可以使一段區間內的所有數加一(模四)。
問最少的操作次數。
思考歷程
一看這題目,誒,這不就是那道叫密碼鎖的題目嗎?
然后隨便打一打,樣例過了,就再也沒有思考這一題。
正解
其實我的想法完全錯了。
因為這題只能加,不能減啊!
于是就得考慮另一個方法。
題目可以轉成這樣的問題:給你一個數列,你可以預先給其中的數加四,然后每次對一個區間進行減一操作,問最少的操作數。
顯然,如果已經加四了,就是一道大水題(好像叫……粉刷柵欄)?
我們先不考慮加四,那么答案就是∑max(ai?ai?1,0)\sum{max(a_i-a_{i-1},0)}∑max(ai??ai?1?,0)
然后我們考慮加四會有什么影響。
現在我們考慮一下,假設有兩個高地為lll和rrr,中間的比較低,要把它們降下來,能不能通過抬高中間的,使得操作數盡量小呢?
然后開始按照lll和l+1l+1l+1之差和r?1r-1r?1和rrr之差分類討論。
接著就可以發現,只有(?3,3)(-3,3)(?3,3)(?2,3)(-2,3)(?2,3)(?3,2)(-3,2)(?3,2)的情況是有意義的。
于是我們掃一掃有沒有這樣的東西,減去它們的貢獻就好了。
要注意,如果一起做,有可能搞完了(?2,3)(-2,3)(?2,3),就沒辦法搞(?3,3)(-3,3)(?3,3)了。
由于(?3,3)(-3,3)(?3,3)更優,所以先從左到右將它給搞掉。
接著重新搞剩下兩種。
然后就可以做出來了,說實在的,這方法讓我醉了……
還有一種比較強大的做法,沒有分類討論。
剛開始的操作是一樣的,同樣是計算出一個暫時的答案。
然后從前往后掃,如果現在走的是下坡路,就將高度差ai?ai?1+4a_i-a_{i-1}+4ai??ai?1?+4存入一個桶中。
如果在走上坡路,記高度差為xxx,就在桶種找小于xxx的第一個有值的,記為jjj。
如果找到了就讓答案減去x?jx-jx?j,然后xxx在桶中的值減一,jjj在桶中的值加一。
這樣就可以計算出答案了。
具體原因什么的……感覺上有些玄學,我是感性理解的。
代碼
using namespace std; #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 int n; int a[N],c[N]; int las[N]; int main(){int T;scanf("%d",&T);while (T--){scanf("%d",&n);for (int i=1;i<=n;++i)scanf("%d",&a[i]);for (int i=1;i<=n;++i){int x;scanf("%d",&x);a[i]=(x-a[i]+4)%4;}int ans=0;for (int i=0;i<=n;++i){ans+=max(a[i+1]-a[i],0);c[i]=a[i+1]-a[i];}memset(las,255,sizeof las);int cnt2=0,cnt3=0;for (int i=0,j=-1;i<=n;++i)if (c[i]==-3){cnt3++;las[i]=j;j=i;}else if (c[i]==3 && cnt3){cnt3--;c[j]=c[i]=0;j=las[j];ans-=2;}cnt3=0;for (int i=0;i<=n;++i)if (c[i]==-2)cnt2++;else if (c[i]==-3)cnt3++;else if (c[i]==2 && cnt3)cnt3--,ans--;else if (c[i]==3 && cnt2)cnt2--,ans--;printf("%d\n",ans);}return 0; } using namespace std; #include <cstdio> #include <cstring> #include <algorithm> #define N 100010 int n; int a[N]; int buc[4]; int main(){int T;scanf("%d",&T);while (T--){scanf("%d",&n);for (int i=1;i<=n;++i)scanf("%d",&a[i]);for (int i=1;i<=n;++i){int x;scanf("%d",&x);a[i]=(x-a[i]+4)%4;}int ans=0;for (int i=1;i<=n;++i)ans+=max(a[i]-a[i-1],0);memset(buc,0,sizeof buc);for (int i=1;i<=n;++i)if (a[i-1]>a[i])buc[a[i]-a[i-1]+4]++;else{int j=0;for (;j<a[i]-a[i-1];++j)if (buc[j])break;if (buc[j]){buc[j]--,buc[a[i]-a[i-1]]++;ans-=a[i]-a[i-1]-j;}}printf("%d\n",ans);}return 0; }總結
分類討論是一種很惡心的方法……
另一種說法叫做:面向數據編程
轉載于:https://www.cnblogs.com/jz-597/p/11145216.html
總結
以上是生活随笔為你收集整理的[JZOJ4788] 【NOIP2016提高A组模拟9.17】序列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .Net高并发解决思路(转)
- 下一篇: 胡润富豪榜2020出炉,雷军身价是任正非