發壽司 - Handing Out Sushi - Indexed Tree(延遲更新)
發壽司
(測試用例總數:X,1.5 秒 (C/C++),2 秒 (Java)/記憶體限制:256 M,Stack 1 M)
麗雅開了一家壽司店,為了宣傳店鋪,她想在開業前準備舉辦一場活動,邀請N位當地居民免費品嚐壽司。
為了增加活動的吸引力,她準備按照如下規則來發壽司。首先,讓N名參與活動的人排成一隊,每人抽取一張寫著編號的紙。(編號可能相同。)如果某個人的編號比他前面以及後面的K個人的排序高(編號越小,排序越低),那麼這個人拿到的壽司數量比排序低的人多。
麗雅準備按照上述規則來開展活動,同時希望發出去的壽司數量儘可能少。幫助麗雅求出此次活動需要準備的最少壽司數量。
假設當地有7名居民參加了活動,K為2。當7個人抽取的編號分別為 4、3、2、4、1、4、3時,第5個居民的排序為1,因為其他人的排序都比1高,所以他只能獲得一個壽司。對於第3個居民,他前面兩個人的排序都比他高,後面兩個人中有一個居民抽取了更低的排序1,這個人獲得了一個壽司,所以第3個居民會獲得兩個壽司。第7個居民也會獲得兩個壽司,因為他前面的兩個人中有一個人的排序比他的更低,而那個排序更低的人獲得了一個壽司。按這種方式計算,可以得出發給參與者的最少壽司數量為 19,如下所示。
1) 第 1 個居民:獲得四個壽司
2) 第 2 個居民:獲得三個壽司
3) 第 3 個居民:獲得兩個壽司
4) 第 4 個居民:獲得四個壽司
5) 第 5 個居民:獲得一個壽司
6) 第 6 個居民:獲得三個壽司
7) 第 7 個居民:獲得兩個壽司
[限制條件]
1.參與者數量N為介於1到100,000之間的整數。
2.比較排序的人數K為介於1到100,000之間的整數。
3.每個參與者抽取的編號為介於1到N之間的整數。
(1是最低的編號,編號越大,排序越高。)
4.所有參與者至少會獲得一個壽司。
[輸入]
首先給出測試用例數量T,接著給出T種測試用例。每個測試用例的第一行,給出參與者數量N 和比較排序的人數K。第二行空格區分給出N名參與者抽取的編號。
[輸出]
各測試用例輸出一行。首先輸出“#x”(x為測試用例的編號,從1開始),加一個空格後,輸出麗雅需要準備的最少壽司數量。
[輸入輸出 示例]
(輸入)
2
7 2
4 3 2 4 1 4 3
7 1
4 3 2 4 1 4 3
(輸出)
#1 19
#2 12
思路分析:
根據題目意思,可知,需要按照排序大小計算,因此先對原資料進行排序,值正序,index逆序
然後求最長遞增子序列,第3個用例分析可知,值相同的節點,前面的節點會影響後面的節點,因此需要延遲更新,即,遇到下一個值不同的節點時,再更新當前值的所有節點
用Indexed Tree求當前節點-K~當前節點+K個區間的LIS,加延遲更新
package tree; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.Queue; import java.util.StringTokenizer; /** * 思路分析: * 根據題目意思可知,需要按照排序大小計算,因此先對原資料進行排序,值正序,index逆序 * 然後求最長遞增子序列,分析用例可知,值相同的節點,前面的節點會影響後面的節點,因此需要延遲更新,即,遇到下一個值不同的節點時,再更新當前值的所有節點 * 用Indexed Tree求當前節點-K~當前節點+K個區間的LIS,加延遲更新 * @author XA-GDD * */ public class EQ_HandingOutSushi_0615 { static int T,N,K; static int _max_Nval = 100000; static int [][] srcArr = new int [_max_Nval][2]; static int [] lisIdxTree = new int[_max_Nval*4]; static int offset; static Queue<int []> queue = new LinkedList<int[]>(); //延遲更新用Queue,勿使用HashMap,效率太慢 static long ANS; public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("D:\\workspace\\sw_pro\\test_case\\sample_input_0615.txt")); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); T = Integer.parseInt(st.nextToken()); for(int testCase = 1; testCase<=T;testCase++) { st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); K = Integer.parseInt(st.nextToken()); ANS=0L; for(int i=0;i<N;i++) { Arrays.fill(srcArr[i], 0); } Arrays.fill(lisIdxTree, 0); st = new StringTokenizer(br.readLine()); for(int i=0;i<N;i++) { srcArr[i][0] = i; srcArr[i][1] = Integer.parseInt(st.nextToken()); } //排序,value正序,index逆序 Arrays.sort(srcArr,0,N, new Comparator<int []>() { @Override public int compare(int[] o1, int[] o2) { if(o1[1]==o2[1]) { return o2[0] - o1[0]; } return o1[1] - o2[1]; } }); //初始化index tree int k=0; while((1<<k)<N) { k++; } offset = 1<<k; int preVal=0; for(int i=0;i<N;i++) { int currVal = srcArr[i][1]; if(currVal!=preVal) { //如果原陣列當前節點和前一個節點不相同時,更新與前一個節點相同的所有節點的值 delayedUpdate(); //如果原陣列當前節點和前一個節點不相同時,開始記錄當前節點的值 queue.clear(); } int start = srcArr[i][0]-K>0?srcArr[i][0]-K:0; int end = srcArr[i][0]+K<offset-1?srcArr[i][0]+K:offset-1; int val = query(start+offset,end+offset); queue.add(new int[] {srcArr[i][0], val+1}); preVal = currVal; } delayedUpdate(); //更新最後一組值 queue.clear(); System.out.printf("#%d %d\n",testCase,ANS); } } static void delayedUpdate() { while(!queue.isEmpty()) { int obj [] = queue.poll(); update(offset+obj[0],obj[1]); ANS += (long)obj[1]; } } static void update(int index ,int val) { lisIdxTree[index] = val; index = index >>1; while(index>0) { lisIdxTree[index] = Math.max(lisIdxTree[index*2], lisIdxTree[index*2+1]); index = index >>1; } } static int query(int start ,int end) { int res=0; while(start<=end) { if(start%2==1) { res = Math.max(res, lisIdxTree[start]); } if(end%2==0) { res = Math.max(res, lisIdxTree[end]); } start = (start+1)>>1; end = (end-1)>>1; } return res; } }