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 }

时间: 2024-08-05 19:28:56

codeforces:Prefix Sums的相关文章

CodeForces 837F - Prefix Sums | Educational Codeforces Round 26

按tutorial打的我血崩,死活挂第四组- - 思路来自FXXL /* CodeForces 837F - Prefix Sums [ 二分,组合数 ] | Educational Codeforces Round 26 题意: 设定数组 y = f(x) 使得 y[i] = sum(x[j]) (0 <= j < i) 求初始数组 A0 经过多少次 f(x) 后 会有一个元素 大于 k 分析: 考虑 A0 = {1, 0, 0, 0} A1 = {1, 1, 1, 1} -> {C(

Codeforces 223APartial Sums 数论+组合数学

题意很简单,求不是那么好求的,k很大 要操作很多次,所以不可能直接来的,印象中解决操作比较多无非线段树 循环节 矩阵 组合数等等吧,这道题目 也就只能多画画什么 的了 就以第一个案例为主吧 , 3 1 2 3 k我们依据画的次数来自己定好了 下面的每个数表示这个位置的 数由最初的 数组num[]中多少个数加起来得到的 当k为0的时候呢,就是 1 1 1 k为1的时候呢 1 2 3 k为2的时候呢 1 3 6 那么k为3的时候 1 4 10 这里看一下 从数组下标0开始,那么其实就是 C(i +

codeforces 509C Sums of Digits

codeforces 509C Sums of Digits 题意: 给出n个数字各位的加和bi,求一个严格递增的数列.要求最后一个数字最小. 如: 3 2 1 -> 3 11 100 限制: 1 <= n <= 300; 1 <= bi <=300 思路: 贪心,要求最后一个数字最小,只要保证一路过来的数字都尽量小就行. 令d=b[i]-b[i-1], 如果d>0,则从最低位填起,尽量把低位填到9 如果d<=0,则先从低位开始进位,使得d>0,然后就可以转

CodeForces - 1204E Natasha, Sasha and the Prefix Sums (组合数学,卡特兰数扩展)

题意:求n个1,m个-1组成的所有序列中,最大前缀之和. 首先引出这样一个问题:使用n个左括号和m个右括号,组成的合法的括号匹配(每个右括号都有对应的左括号和它匹配)的数目是多少? 1.当n=m时,显然答案为卡特兰数$C_{2n}^{n}-C_{2n}^{n+1}$ 2.当n<m时,无论如何都不合法,答案为0 3.当n>m时,答案为$C_{n+m}^{n}-C_{n+m}^{n+1}$,这是一个推论,证明过程有点抽象,方法是把不合法的方案数等价于从(-2,0)移动到(n+m,n-m)的方案数,

GenomicRangeQuery /codility/ preFix sums

首先上题目: A DNA sequence can be represented as a string consisting of the letters A, C, G and T, which correspond to the types of successive nucleotides in the sequence. Each nucleotide has an impact factor, which is an integer. Nucleotides of types A,

[CF1204E]Natasha,Sasha and the Prefix Sums 题解

前言 本文中的排列指由n个1, m个-1构成的序列中的一种. 题目这么长不吐槽了,但是这确实是一道好题. 题解 DP题话不多说,直接状态/变量/转移. 状态 我们定义f表示"最大prefix sum"之和 变量 f[i][j]为有i个1,j个-1的"最大prefix sum"之和 转移 我们记C[i][j]为\(\left(\begin{matrix} i \\ j\end{matrix}\right)\),那么: \[f[i][j] = \left\{\begin

Codeforces 509C Sums of Digits 贪心

这道题目有人用DFS.有人用DP 我觉得还是最简单的贪心解决也是不错的选择. Ok,不废话了,这道题目的意思就是 原先存在一个严格递增的Arrary_A,然后Array_A[i] 的每位之和为Array_B[i] 现在给你一个Array_B, 让你在条件: Array_A[len] Minimize 下求出次数组 (当然我们很容易得出,如果Array_A[len] 不是最小化的,那么答案有无穷多,随意暴力一下都可以) 所以这题没有那么暴力= = 解题思路: 首先求出Array_B[i] 和 Ar

E. Natasha, Sasha and the Prefix Sums

给定n个 1 m个 -1的全排 求所有排列的$f(a)=max(0,max_{1≤i≤l}∑_{j=1}^{i}a_{j})$之和 组合数,枚举 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll MOD = 998244853; int n, m; ll C[4002][4002]; ll sum; ll realSum; ll ans; void init() { for(int i=0;

CF1024E Natasha, Sasha and the Prefix Sums——DP/数学(组合数)

题面 CF1024E 解析 题意就是要求所有由$n$个$1$.$m$个$-1$构成的序列的最大前缀和的和 算法一$(DP)$ $n$, $m$都小于等于$2000$, 显然可以$DP$ 设$dp[i][j]$表示由$i$个$1$, $j$个$-1$构成的序列的最大前缀和的和 $i$个$1$, $j$个$-1$构成的序列, 可以看做是在$i-1$个$1$, $j$个$-1$的序列的最前面加一个$1$得到,也可以看做是在$i$个$1$, $j-1$个$-1$的序列最前面加一个$-1$得到 这也就意味