ZeroJudge f638 - 支點切割 解題心得( 題目 )
解題概念:前綴和
解題方法:我們先把前綴和(記做ls)跟後綴和(記做rs)做一次,然後分別對於他們兩個再做前綴和(lss)、後綴和(記做rss),這樣的好處是,如果先假設今天陣列範圍是0-n,則如果假設我要切陣列第3個,我們求的a0*3+a1*2+a2*1就是(a0+a1+a2)+(a0+a1)+(a0)=ls[2]+ls[1]+ls[0]=lss[2],因右邊概念一樣,我們就可以在O(1)的時間記錄一切點的cost,線性去尋找哪個比較大。
再來如果範圍不是全部呢?假設我們切i,我們左邊其實就是lss[i-1]-lss[l-1]-ls[l-1]*(i-l),原因是下圖的三角形減長方形,他會互消變成我們要的答案,所以一個點的查詢也是O(1)。
Java參考程式碼(註:下面程式為方便實作是1-based)
import java.io.*; public class d004 { public static BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); public static int ret,r,k; public static long rt=0; public static int[] mp=new int[50005]; public static long[] ls=new long[50005]; public static long[] rs=new long[50005]; public static long[] lss=new long[50005]; public static long[] rss=new long[50005]; public static void main(String[] args) throws Exception{ final int n=orz(); k=orz(); for(int i=1;i<=n;i++)mp[i]=orz(); ls[0]=lss[0]=0; rs[n+1]=rss[n+1]=0; for(int i=1;i<=n;i++)ls[i]=ls[i-1]+mp[i]; for(int i=1;i<=n;i++)lss[i]=lss[i-1]+ls[i]; for(int i=n;i>0;i--)rs[i]=rs[i+1]+mp[i]; for(int i=n;i>0;i--)rss[i]=rss[i+1]+rs[i]; cut(1,n,0); System.out.println(rt); } public static void cut(int l,int r,int c) { if(c==k||l+1>=r)return; long min=1L<<60,t; int p=-1; for(int i=l+1;i<r;i++) { t=Math.abs(lss[i-1]-lss[l-1]-ls[l-1]*(i-l)-rss[i+1]+rss[r+1]+rs[r+1]*(r-i)); if(t<min) { min=t; p=i; } } rt+=mp[p]; cut(l,p-1,c+1); cut(p+1,r,c+1); } public static int orz() throws Exception{ ret=0; r=br.read(); while(r>47&&r<58) { ret*=10; ret+=(r&15); r=br.read(); } return ret; } }
文章標籤
全站熱搜