今天我們來看看CF1257E
題目連結html
題目
有三個籃子,放數字\(1\sim n\)。求最小的數字移動次數,使得三個籃子裡的數字由小排到大。linux
好久沒有寫題目了,主要是本身本科也不是作電腦的,並且寫難度2000的題目真的遇到不少挫折...,經常寫到想放棄。
這題也搞了好久,看了別人的解析才知道怎麼作,腦袋打結了一成天。
ios
這題其實有dp解法,可是這篇文章就不講了。
第一眼看到這題目,可能會覺得就是要找到一組數字\([l,r]\)表示第一個籃子放\([1,l]\),第二個籃子放\([l+1,r-1]\),第三個籃子放\([r,n]\)。可是\(n\le2e5\),因此我們應該是遍歷\(l\)從\(1\sim n\),然後快速地找出哪個\(r\)最好。
令\(cnt[i][j]\)表示第\(i\)個籃子擁有幾個\([1,j]\)的數字。
假設目前決定好了\([l,r]\),那麼須要的步數即:\(cnt[1][l+1\sim n]+cnt[2][1\sim l]+cnt[2][r\sim n]+cnt[3][1\sim r-1]\)
也就是我們計算要幾步才能把本來不在正確的籃子裡的數字放到正確的籃子裡。
假設已經固定了\(l\),\(cnt[1][l+1\sim n]+cnt[2][1\sim l]\)就能夠用前綴和輕鬆算出來,接著我們只要決定\(cnt[2][r\sim n]+cnt[3][1\sim r-1]\)究竟是哪個\(r\)才會最小就好。
而只要事先計算對於每個後綴\([r\sim n]\),最小的\(cnt[2][r\sim n]+cnt[3][1\sim r-1]\)是多少,就能夠解決這題了。spa
const int _n=2e5+10; int t,n,m,k[3],pos[_n],cnt[3][_n],mn[_n],ans=0x7f7f7f7f; main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0); rep(_,0,3){cin>>k[_];n+=k[_];} rep(_,0,3)rep(i,0,k[_]){cin>>t;pos[t]=_;} rep(i,1,n+1)rep(_,0,3)cnt[_][i]=cnt[_][i-1]+(pos[i]==_); mn[n+1]=cnt[2][n]; per(i,1,n+1)mn[i]=min(mn[i+1],cnt[1][n]-cnt[1][i-1]+cnt[2][i-1]); rep(i,0,n+1)ans=min(ans,cnt[0][n]-cnt[0][i]+cnt[1][i]+mn[i+1]); cout<<ans; return 0; }
標頭、模板請點Submission看
Submissioncode