codeforces:Prefix Sums

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codeforces:Prefix Sums相关的知识,希望对你有一定的参考价值。

题目大意:

  给出一个函数P,P接受一个数组A作为参数,并返回一个新的数组B,且B.length = A.length + 1,B[i] = SUM(A[0], ..., A[i])。有一个无穷数组序列A[0], A[1], ... 满足A[i]=P(A[i-1]),其中i为任意自然数。对于输入k和A[0],求一个最小的下标t,使得A[t]中包含不小于k的数值。

  其中A[0].length <= 2e5, k <= 1e18,且A[0]中至少有两个正整数。


  数学向的题目。本来以为是个找规律的题目,但是最后并不能找到,只好参考了官方题解的。总的来说很有趣的题目。

  对于输入的A[0],首先需要把前面的0去除,因为这些0没有任何意义(若A[0][0], ..., A[0][t] = 0,则A[i][0], ... ,A[i][t] = 0),但是会拖慢程序。

  接下来,可以依据A[0]的长度做判断。如果长度超过某个阈值U,则说明这个序列中最大值将会再后续的迭代中快速增长,增长速度与序列的长度L有关,我个人的估计是O(t^L)级别的,其中t为迭代次数,这里不给证明。因此可以暴力循环直到出现不小于k的元素即可。官方推荐的阈值为10。

  而对于A[0]的长度不超过10的情况,需要借助矩阵来求解。由于P(x1,x2, ... , xn) = (x1, x1 + x2, ... , x1 + x2 + ... + xn),显然P是一个线性变换,线性代数教过每个线性变换都唯一对应一个矩阵。下面给出对应的线性变换的公式:$$ \left(\begin{matrix} 1 & 0 &\cdots & 0 & 0\\ 1 & 1 &\cdots & 0 & 0\\ \cdots &\cdots &\cdots &\cdots &\cdots\\ 1 & 1 &\cdots & 1 & 0\\ 1 & 1 &\cdots & 1 & 1 \end{matrix}\right)\cdot A\left[i\right]=A\left[i+1\right] $$

  之后记变换矩阵为T。P^n(A[0])=T^n*A[0],其中T^n可以利用快速幂乘法计算得到(理由是矩阵的乘法运算是结合的)。之后利用二分查找法寻找最小的x,使得T^x*A[0]中存在不小于k的元素。在这个过程中为了提高效率,可以使用红黑树缓存中间计算过的矩阵。

  最后说明一下时间复杂度,暴力破解部分不进行说明,只说明利用矩阵计算的部分。首先说明二分查找法迭代的次数,由于MAX(P(A[i]))>MAX(A[i]),因此最终解必然不可能超过k,而二分查找法的迭代次数则为O(log2(k))。而由于二分查找法每次迭代都需要计算中间值,中间值的矩阵M必须得到计算,借助缓存,可以认为形如T^(2^i)的矩阵均已经被缓存,故计算中间值矩阵的时间复杂度为O(log2(k))*O(n^3)=O(n^3log2(k)),而判断M*A[0]中是否有达到k的元素这一过程的时间复杂度可以不考虑(因为是小头)。故整个二分查找法的时间复杂度为O(log2(k))*O(n^3log2(k))=O(n^3(log2(k))^2),这在已知n<=10的前提下是可以接受的。


  最后给出JAVA代码:

技术分享
  1 package cn.dalt.codeforces;
  2 
  3 import java.io.BufferedInputStream;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.PushbackInputStream;
  7 import java.math.BigDecimal;
  8 import java.util.Map;
  9 import java.util.TreeMap;
 10 
 11 /**
 12  * Created by dalt on 2017/9/10.
 13  */
 14 public class BruteForcePrefixSums {
 15     int n;
 16     long threshold;
 17     long[] basic;
 18 
 19     public static void main(String[] args) throws Exception {
 20         BruteForcePrefixSums solution = new BruteForcePrefixSums();
 21         solution.init();
 22         long result = solution.solve();
 23         System.out.println(result);
 24     }
 25 
 26     public void init() throws Exception {
 27         AcmInputReader input = new AcmInputReader(System.in);
 28         n = input.nextInteger();
 29         threshold = input.nextLong();
 30         basic = new long[n];
 31 
 32         for (int i = 0; i < n; i++) {
 33             basic[i] = input.nextLong();
 34         }
 35     }
 36 
 37     public long solve() {
 38         //Remove prefix blank
 39         {
 40             int firstNotZero = 0;
 41             while (basic[firstNotZero] == 0) {
 42                 firstNotZero++;
 43             }
 44             long[] tmp = new long[n - firstNotZero];
 45             System.arraycopy(basic, firstNotZero, tmp, 0, tmp.length);
 46             n = tmp.length;
 47             basic = tmp;
 48         }
 49 
 50         //Test whether A0 satisfy the threshold
 51         for (long value : basic) {
 52             if (value >= threshold) {
 53                 return 0;
 54             }
 55         }
 56 
 57         //n is more than 18, so brute force
 58         if (n >= 10) {
 59             return bruteForceProcess();
 60         }
 61         //Try fast matrix
 62         else {
 63             return binarySearch(basic);
 64         }
 65     }
 66 
 67     public long binarySearch(long[] vector) {
 68         long[][] a0 = new long[vector.length][vector.length];
 69         for (int i = 0, bound = vector.length; i < bound; i++) {
 70             for (int j = 0; j <= i; j++) {
 71                 a0[i][j] = 1;
 72             }
 73         }
 74 
 75         TreeMap<Long, long[][]> cache = new TreeMap<>();
 76         cache.put(1L, a0);
 77 
 78         //Find lower bound and upper bound
 79         long lowerAge = 0;
 80         long[][] lowerMat = null;
 81         long upperAge = 1;
 82         long[][] upperMat = a0;
 83         while (!contain(upperMat, vector)) {
 84             lowerMat = upperMat;
 85             lowerAge = upperAge;
 86             cache.put(lowerAge, lowerMat);
 87             upperMat = multiply(upperMat, upperMat);
 88             upperAge *= 2;
 89         }
 90 
 91         //Binary search part
 92         while (lowerAge < upperAge) {
 93             long halfAge = (lowerAge + upperAge + 1) / 2;
 94 
 95             //Calculate a0^half in fast way
 96             Map.Entry<Long, long[][]> entry = cache.floorEntry(halfAge);
 97             long remain = halfAge - entry.getKey();
 98             long[][] halfMat = entry.getValue();
 99             while (remain > 0) {
100                 entry = cache.floorEntry(remain);
101                 remain = remain - entry.getKey();
102                 halfMat = multiply(halfMat, entry.getValue());
103             }
104 
105             if (contain(halfMat, vector)) {
106                 upperAge = halfAge - 1;
107                 upperMat = halfMat;
108             } else {
109                 lowerAge = halfAge;
110                 lowerMat = halfMat;
111             }
112         }
113         return lowerAge + 1;
114     }
115 
116     public boolean contain(long[][] mat, long[] vector) {
117         long[] result = new long[vector.length];
118         int row = mat.length;
119         long max = 0;
120         int col = mat[0].length;
121         for (int i = 0; i < row; i++) {
122             long aggregation = 0;
123             for (int j = 0; j < col; j++) {
124                 long value = mat[i][j] * vector[j];
125                 if (value >= threshold) {
126                     return true;
127                 }
128                 aggregation += mat[i][j] * vector[j];
129             }
130             if (aggregation < 0 || aggregation >= threshold) {
131                 return true;
132             }
133         }
134         return false;
135     }
136 
137     public long[][] multiply(long[][] a, long[][] b) {
138         long[][] result = new long[a.length][b[0].length];
139         int row = a.length;
140         int col = b[0].length;
141         int mid = b.length;
142         for (int i = 0; i < row; i++) {
143             for (int j = 0; j < col; j++) {
144                 long aggregation = 0;
145                 for (int k = 0; k < mid; k++) {
146                     aggregation += a[i][k] * b[k][j];
147                 }
148                 result[i][j] = aggregation < 0 ? Long.MAX_VALUE : aggregation;
149             }
150         }
151         return result;
152     }
153 
154     public long bruteForceProcess() {
155         int age = 0;
156         while (true) {
157             age++;
158             for (int i = 1; i < n; i++) {
159                 basic[i] = basic[i - 1] + basic[i];
160                 if (basic[i] >= threshold) {
161                     return age;
162                 }
163             }
164         }
165     }
166 
167     /**
168      * @author dalt
169      * @see java.lang.AutoCloseable
170      * @since java1.7
171      */
172     static class AcmInputReader implements AutoCloseable {
173         private PushbackInputStream in;
174 
175         /**
176          * 创建读取器
177          *
178          * @param input 输入流
179          */
180         public AcmInputReader(InputStream input) {
181             in = new PushbackInputStream(new BufferedInputStream(input));
182         }
183 
184         @Override
185         public void close() throws IOException {
186             in.close();
187         }
188 
189         private int nextByte() throws IOException {
190             return in.read() & 0xff;
191         }
192 
193         /**
194          * 如果下一个字节为b,则跳过该字节
195          *
196          * @param b 被跳过的字节值
197          * @throws IOException if 输入流读取错误
198          */
199         public void skipByte(int b) throws IOException {
200             int c;
201             if ((c = nextByte()) != b) {
202                 in.unread(c);
203             }
204         }
205 
206         /**
207          * 如果后续k个字节均为b,则跳过k个字节。这里{@literal k<times}
208          *
209          * @param b     被跳过的字节值
210          * @param times 跳过次数,-1表示无穷
211          * @throws IOException if 输入流读取错误
212          */
213         public void skipByte(int b, int times) throws IOException {
214             int c;
215             while ((c = nextByte()) == b && times > 0) {
216                 times--;
217             }
218             if (c != b) {
219                 in.unread(c);
220             }
221         }
222 
223         /**
224          * 类似于{@link #skipByte(int, int)}, 但是会跳过中间出现的空白字符。
225          *
226          * @param b     被跳过的字节值
227          * @param times 跳过次数,-1表示无穷
228          * @throws IOException if 输入流读取错误
229          */
230         public void skipBlankAndByte(int b, int times) throws IOException {
231             int c;
232             skipBlank();
233             while ((c = nextByte()) == b && times > 0) {
234                 times--;
235                 skipBlank();
236             }
237             if (c != b) {
238                 in.unread(c);
239             }
240         }
241 
242         /**
243          * 读取下一块不含空白字符的字符块
244          *
245          * @return 下一块不含空白字符的字符块
246          * @throws IOException if 输入流读取错误
247          */
248         public String nextBlock() throws IOException {
249             skipBlank();
250             StringBuilder sb = new StringBuilder();
251             int c = nextByte();
252             while (AsciiMarksLazyHolder.asciiMarks[c = nextByte()] != AsciiMarksLazyHolder.BLANK_MARK) {
253                 sb.append((char) c);
254             }
255             in.unread(c);
256             return sb.toString();
257         }
258 
259         /**
260          * 跳过输入流中后续空白字符
261          *
262          * @throws IOException if 输入流读取错误
263          */
264         private void skipBlank() throws IOException {
265             int c;
266             while ((c = nextByte()) <= 32) ;
267             in.unread(c);
268         }
269 
270         /**
271          * 读取下一个整数(可正可负),这里没有对溢出做判断
272          *
273          * @return 下一个整数值
274          * @throws IOException if 输入流读取错误
275          */
276         public int nextInteger() throws IOException {
277             skipBlank();
278             int value = 0;
279             boolean positive = true;
280             int c = nextByte();
281             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
282                 positive = c == ‘+‘;
283             } else {
284                 value = ‘0‘ - c;
285             }
286             c = nextByte();
287             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
288                 value = (value << 3) + (value << 1) + ‘0‘ - c;
289                 c = nextByte();
290             }
291 
292             in.unread(c);
293             return positive ? -value : value;
294         }
295 
296         /**
297          * 判断是否到了文件结尾
298          *
299          * @return true如果到了文件结尾,否则false
300          * @throws IOException if 输入流读取错误
301          */
302         public boolean isMeetEOF() throws IOException {
303             int c = nextByte();
304             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
305                 return true;
306             }
307             in.unread(c);
308             return false;
309         }
310 
311         /**
312          * 判断是否在跳过空白字符后抵达文件结尾
313          *
314          * @return true如果到了文件结尾,否则false
315          * @throws IOException if 输入流读取错误
316          */
317         public boolean isMeetBlankAndEOF() throws IOException {
318             skipBlank();
319             int c = nextByte();
320             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
321                 return true;
322             }
323             in.unread(c);
324             return false;
325         }
326 
327         /**
328          * 获取下一个用英文字母组成的单词
329          *
330          * @return 下一个用英文字母组成的单词
331          */
332         public String nextWord() throws IOException {
333             StringBuilder sb = new StringBuilder(16);
334             skipBlank();
335             int c;
336             while ((AsciiMarksLazyHolder.asciiMarks[(c = nextByte())] & AsciiMarksLazyHolder.LETTER_MARK) != 0) {
337                 sb.append((char) c);
338             }
339             in.unread(c);
340             return sb.toString();
341         }
342 
343         /**
344          * 读取下一个长整数(可正可负),这里没有对溢出做判断
345          *
346          * @return 下一个长整数值
347          * @throws IOException if 输入流读取错误
348          */
349         public long nextLong() throws IOException {
350             skipBlank();
351             long value = 0;
352             boolean positive = true;
353             int c = nextByte();
354             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
355                 positive = c == ‘+‘;
356             } else {
357                 value = ‘0‘ - c;
358             }
359             c = nextByte();
360             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
361                 value = (value << 3) + (value << 1) + ‘0‘ - c;
362                 c = nextByte();
363             }
364             in.unread(c);
365             return positive ? -value : value;
366         }
367 
368         /**
369          * 读取下一个浮点数(可正可负),浮点数是近似值
370          *
371          * @return 下一个浮点数值
372          * @throws IOException if 输入流读取错误
373          */
374         public float nextFloat() throws IOException {
375             return (float) nextDouble();
376         }
377 
378         /**
379          * 读取下一个浮点数(可正可负),浮点数是近似值
380          *
381          * @return 下一个浮点数值
382          * @throws IOException if 输入流读取错误
383          */
384         public double nextDouble() throws IOException {
385             skipBlank();
386             double value = 0;
387             boolean positive = true;
388             int c = nextByte();
389             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
390                 positive = c == ‘+‘;
391             } else {
392                 value = c - ‘0‘;
393             }
394             c = nextByte();
395             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
396                 value = value * 10.0 + c - ‘0‘;
397                 c = nextByte();
398             }
399 
400             if (c == ‘.‘) {
401                 double littlePart = 0;
402                 double base = 1;
403                 c = nextByte();
404                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
405                     littlePart = littlePart * 10.0 + c - ‘0‘;
406                     base *= 10.0;
407                     c = nextByte();
408                 }
409                 value += littlePart / base;
410             }
411             in.unread(c);
412             return positive ? value : -value;
413         }
414 
415         /**
416          * 读取下一个高精度数值
417          *
418          * @return 下一个高精度数值
419          * @throws IOException if 输入流读取错误
420          */
421         public BigDecimal nextDecimal() throws IOException {
422             skipBlank();
423             StringBuilder sb = new StringBuilder();
424             sb.append((char) nextByte());
425             int c = nextByte();
426             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
427                 sb.append((char) c);
428                 c = nextByte();
429             }
430             if (c == ‘.‘) {
431                 sb.append(‘.‘);
432                 c = nextByte();
433                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
434                     sb.append((char) c);
435                     c = nextByte();
436                 }
437             }
438             in.unread(c);
439             return new BigDecimal(sb.toString());
440         }
441 
442         private static class AsciiMarksLazyHolder {
443             public static final byte BLANK_MARK = 1;
444             public static final byte SIGN_MARK = 1 << 1;
445             public static final byte NUMERAL_MARK = 1 << 2;
446             public static final byte UPPERCASE_LETTER_MARK = 1 << 3;
447             public static final byte LOWERCASE_LETTER_MARK = 1 << 4;
448             public static final byte LETTER_MARK = UPPERCASE_LETTER_MARK | LOWERCASE_LETTER_MARK;
449             public static final byte EOF = 1 << 5;
450             public static byte[] asciiMarks = new byte[256];
451 
452             static {
453                 for (int i = 0; i <= 32; i++) {
454                     asciiMarks[i] = BLANK_MARK;
455                 }
456                 asciiMarks[‘+‘] = SIGN_MARK;
457                 asciiMarks[‘-‘] = SIGN_MARK;
458                 for (int i = ‘0‘; i <= ‘9‘; i++) {
459                     asciiMarks[i] = NUMERAL_MARK;
460                 }
461                 for (int i = ‘a‘; i <= ‘z‘; i++) {
462                     asciiMarks[i] = LOWERCASE_LETTER_MARK;
463                 }
464                 for (int i = ‘A‘; i <= ‘Z‘; i++) {
465                     asciiMarks[i] = UPPERCASE_LETTER_MARK;
466                 }
467                 asciiMarks[0xff] = EOF;
468             }
469         }
470     }
471 }
View Code

 

以上是关于codeforces:Prefix Sums的主要内容,如果未能解决你的问题,请参考以下文章

Square Sums UVA - 11470

uva:10487 - Closest Sums(二分查找)

BZOJ 1653 [Usaco2006 Feb]Backward Digit Sums ——搜索

Python没有正确使用变量值

51nod 1161 Partial Sums,1172 Partial Sums V2

POJ 2140 Herd Sums