题目大意:

  给出一个函数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^3*log2(k)),而判断M*A[0]中是否有达到k的元素这一过程的时间复杂度可以不考虑(因为是小头)。故整个二分查找法的时间复杂度为O(log2(k))*O(n^3*log2(k))=O(n^3*(log2(k))^2),这在已知n<=10的前提下是可以接受的。


  最后给出JAVA代码:

 package cn.dalt.codeforces;

 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PushbackInputStream;
 import java.math.BigDecimal;
 import java.util.Map;
 import java.util.TreeMap;

 /**
  * Created by dalt on 2017/9/10.
  */
 public class BruteForcePrefixSums {
     int n;
     long threshold;
     long[] basic;

     public static void main(String[] args) throws Exception {
         BruteForcePrefixSums solution = new BruteForcePrefixSums();
         solution.init();
         long result = solution.solve();
         System.out.println(result);
     }

     public void init() throws Exception {
         AcmInputReader input = new AcmInputReader(System.in);
         n = input.nextInteger();
         threshold = input.nextLong();
         basic = new long[n];

         for (int i = 0; i < n; i++) {
             basic[i] = input.nextLong();
         }
     }

     public long solve() {
         //Remove prefix blank
         {
             int firstNotZero = 0;
             while (basic[firstNotZero] == 0) {
                 firstNotZero++;
             }
             long[] tmp = new long[n - firstNotZero];
             System.arraycopy(basic, firstNotZero, tmp, 0, tmp.length);
             n = tmp.length;
             basic = tmp;
         }

         //Test whether A0 satisfy the threshold
         for (long value : basic) {
             if (value >= threshold) {
                 return 0;
             }
         }

         //n is more than 18, so brute force
         if (n >= 10) {
             return bruteForceProcess();
         }
         //Try fast matrix
         else {
             return binarySearch(basic);
         }
     }

     public long binarySearch(long[] vector) {
         long[][] a0 = new long[vector.length][vector.length];
         for (int i = 0, bound = vector.length; i < bound; i++) {
             for (int j = 0; j <= i; j++) {
                 a0[i][j] = 1;
             }
         }

         TreeMap<Long, long[][]> cache = new TreeMap<>();
         cache.put(1L, a0);

         //Find lower bound and upper bound
         long lowerAge = 0;
         long[][] lowerMat = null;
         long upperAge = 1;
         long[][] upperMat = a0;
         while (!contain(upperMat, vector)) {
             lowerMat = upperMat;
             lowerAge = upperAge;
             cache.put(lowerAge, lowerMat);
             upperMat = multiply(upperMat, upperMat);
             upperAge *= 2;
         }

         //Binary search part
         while (lowerAge < upperAge) {
             long halfAge = (lowerAge + upperAge + 1) / 2;

             //Calculate a0^half in fast way
             Map.Entry<Long, long[][]> entry = cache.floorEntry(halfAge);
             long remain = halfAge - entry.getKey();
             long[][] halfMat = entry.getValue();
             while (remain > 0) {
                 entry = cache.floorEntry(remain);
                 remain = remain - entry.getKey();
                 halfMat = multiply(halfMat, entry.getValue());
             }

             if (contain(halfMat, vector)) {
                 upperAge = halfAge - 1;
                 upperMat = halfMat;
             } else {
                 lowerAge = halfAge;
                 lowerMat = halfMat;
             }
         }
         return lowerAge + 1;
     }

     public boolean contain(long[][] mat, long[] vector) {
         long[] result = new long[vector.length];
         int row = mat.length;
         long max = 0;
         int col = mat[0].length;
         for (int i = 0; i < row; i++) {
             long aggregation = 0;
             for (int j = 0; j < col; j++) {
                 long value = mat[i][j] * vector[j];
                 if (value >= threshold) {
                     return true;
                 }
                 aggregation += mat[i][j] * vector[j];
             }
             if (aggregation < 0 || aggregation >= threshold) {
                 return true;
             }
         }
         return false;
     }

     public long[][] multiply(long[][] a, long[][] b) {
         long[][] result = new long[a.length][b[0].length];
         int row = a.length;
         int col = b[0].length;
         int mid = b.length;
         for (int i = 0; i < row; i++) {
             for (int j = 0; j < col; j++) {
                 long aggregation = 0;
                 for (int k = 0; k < mid; k++) {
                     aggregation += a[i][k] * b[k][j];
                 }
                 result[i][j] = aggregation < 0 ? Long.MAX_VALUE : aggregation;
             }
         }
         return result;
     }

     public long bruteForceProcess() {
         int age = 0;
         while (true) {
             age++;
             for (int i = 1; i < n; i++) {
                 basic[i] = basic[i - 1] + basic[i];
                 if (basic[i] >= threshold) {
                     return age;
                 }
             }
         }
     }

     /**
      * @author dalt
      * @see java.lang.AutoCloseable
      * @since java1.7
      */
     static class AcmInputReader implements AutoCloseable {
         private PushbackInputStream in;

         /**
          * 创建读取器
          *
          * @param input 输入流
          */
         public AcmInputReader(InputStream input) {
             in = new PushbackInputStream(new BufferedInputStream(input));
         }

         @Override
         public void close() throws IOException {
             in.close();
         }

         private int nextByte() throws IOException {
             return in.read() & 0xff;
         }

         /**
          * 如果下一个字节为b,则跳过该字节
          *
          * @param b 被跳过的字节值
          * @throws IOException if 输入流读取错误
          */
         public void skipByte(int b) throws IOException {
             int c;
             if ((c = nextByte()) != b) {
                 in.unread(c);
             }
         }

         /**
          * 如果后续k个字节均为b,则跳过k个字节。这里{@literal k<times}
          *
          * @param b     被跳过的字节值
          * @param times 跳过次数,-1表示无穷
          * @throws IOException if 输入流读取错误
          */
         public void skipByte(int b, int times) throws IOException {
             int c;
             while ((c = nextByte()) == b && times > 0) {
                 times--;
             }
             if (c != b) {
                 in.unread(c);
             }
         }

         /**
          * 类似于{@link #skipByte(int, int)}, 但是会跳过中间出现的空白字符。
          *
          * @param b     被跳过的字节值
          * @param times 跳过次数,-1表示无穷
          * @throws IOException if 输入流读取错误
          */
         public void skipBlankAndByte(int b, int times) throws IOException {
             int c;
             skipBlank();
             while ((c = nextByte()) == b && times > 0) {
                 times--;
                 skipBlank();
             }
             if (c != b) {
                 in.unread(c);
             }
         }

         /**
          * 读取下一块不含空白字符的字符块
          *
          * @return 下一块不含空白字符的字符块
          * @throws IOException if 输入流读取错误
          */
         public String nextBlock() throws IOException {
             skipBlank();
             StringBuilder sb = new StringBuilder();
             int c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c = nextByte()] != AsciiMarksLazyHolder.BLANK_MARK) {
                 sb.append((char) c);
             }
             in.unread(c);
             return sb.toString();
         }

         /**
          * 跳过输入流中后续空白字符
          *
          * @throws IOException if 输入流读取错误
          */
         private void skipBlank() throws IOException {
             int c;
             while ((c = nextByte()) <= 32) ;
             in.unread(c);
         }

         /**
          * 读取下一个整数(可正可负),这里没有对溢出做判断
          *
          * @return 下一个整数值
          * @throws IOException if 输入流读取错误
          */
         public int nextInteger() throws IOException {
             skipBlank();
             int value = 0;
             boolean positive = true;
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
                 positive = c == '+';
             } else {
                 value = '0' - c;
             }
             c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 value = (value << 3) + (value << 1) + '0' - c;
                 c = nextByte();
             }

             in.unread(c);
             return positive ? -value : value;
         }

         /**
          * 判断是否到了文件结尾
          *
          * @return true如果到了文件结尾,否则false
          * @throws IOException if 输入流读取错误
          */
         public boolean isMeetEOF() throws IOException {
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
                 return true;
             }
             in.unread(c);
             return false;
         }

         /**
          * 判断是否在跳过空白字符后抵达文件结尾
          *
          * @return true如果到了文件结尾,否则false
          * @throws IOException if 输入流读取错误
          */
         public boolean isMeetBlankAndEOF() throws IOException {
             skipBlank();
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
                 return true;
             }
             in.unread(c);
             return false;
         }

         /**
          * 获取下一个用英文字母组成的单词
          *
          * @return 下一个用英文字母组成的单词
          */
         public String nextWord() throws IOException {
             StringBuilder sb = new StringBuilder(16);
             skipBlank();
             int c;
             while ((AsciiMarksLazyHolder.asciiMarks[(c = nextByte())] & AsciiMarksLazyHolder.LETTER_MARK) != 0) {
                 sb.append((char) c);
             }
             in.unread(c);
             return sb.toString();
         }

         /**
          * 读取下一个长整数(可正可负),这里没有对溢出做判断
          *
          * @return 下一个长整数值
          * @throws IOException if 输入流读取错误
          */
         public long nextLong() throws IOException {
             skipBlank();
             long value = 0;
             boolean positive = true;
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
                 positive = c == '+';
             } else {
                 value = '0' - c;
             }
             c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 value = (value << 3) + (value << 1) + '0' - c;
                 c = nextByte();
             }
             in.unread(c);
             return positive ? -value : value;
         }

         /**
          * 读取下一个浮点数(可正可负),浮点数是近似值
          *
          * @return 下一个浮点数值
          * @throws IOException if 输入流读取错误
          */
         public float nextFloat() throws IOException {
             return (float) nextDouble();
         }

         /**
          * 读取下一个浮点数(可正可负),浮点数是近似值
          *
          * @return 下一个浮点数值
          * @throws IOException if 输入流读取错误
          */
         public double nextDouble() throws IOException {
             skipBlank();
             double value = 0;
             boolean positive = true;
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
                 positive = c == '+';
             } else {
                 value = c - '0';
             }
             c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 value = value * 10.0 + c - '0';
                 c = nextByte();
             }

             if (c == '.') {
                 double littlePart = 0;
                 double base = 1;
                 c = nextByte();
                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                     littlePart = littlePart * 10.0 + c - '0';
                     base *= 10.0;
                     c = nextByte();
                 }
                 value += littlePart / base;
             }
             in.unread(c);
             return positive ? value : -value;
         }

         /**
          * 读取下一个高精度数值
          *
          * @return 下一个高精度数值
          * @throws IOException if 输入流读取错误
          */
         public BigDecimal nextDecimal() throws IOException {
             skipBlank();
             StringBuilder sb = new StringBuilder();
             sb.append((char) nextByte());
             int c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 sb.append((char) c);
                 c = nextByte();
             }
             if (c == '.') {
                 sb.append('.');
                 c = nextByte();
                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                     sb.append((char) c);
                     c = nextByte();
                 }
             }
             in.unread(c);
             return new BigDecimal(sb.toString());
         }

         private static class AsciiMarksLazyHolder {
             public static final byte BLANK_MARK = 1;
             public static final byte SIGN_MARK = 1 << 1;
             public static final byte NUMERAL_MARK = 1 << 2;
             public static final byte UPPERCASE_LETTER_MARK = 1 << 3;
             public static final byte LOWERCASE_LETTER_MARK = 1 << 4;
             public static final byte LETTER_MARK = UPPERCASE_LETTER_MARK | LOWERCASE_LETTER_MARK;
             public static final byte EOF = 1 << 5;
             public static byte[] asciiMarks = new byte[256];

             static {
                 for (int i = 0; i <= 32; i++) {
                     asciiMarks[i] = BLANK_MARK;
                 }
                 asciiMarks['+'] = SIGN_MARK;
                 asciiMarks['-'] = SIGN_MARK;
                 for (int i = '0'; i <= '9'; i++) {
                     asciiMarks[i] = NUMERAL_MARK;
                 }
                 for (int i = 'a'; i <= 'z'; i++) {
                     asciiMarks[i] = LOWERCASE_LETTER_MARK;
                 }
                 for (int i = 'A'; i <= 'Z'; i++) {
                     asciiMarks[i] = UPPERCASE_LETTER_MARK;
                 }
                 asciiMarks[0xff] = EOF;
             }
         }
     }
 }

codeforces:Prefix Sums分析和实现的更多相关文章

  1. CodeForces 837F - Prefix Sums | Educational Codeforces Round 26

    按tutorial打的我血崩,死活挂第四组- - 思路来自FXXL /* CodeForces 837F - Prefix Sums [ 二分,组合数 ] | Educational Codeforc ...

  2. Educational Codeforces Round 26 [ D. Round Subset ] [ E. Vasya's Function ] [ F. Prefix Sums ]

    PROBLEM D - Round Subset 题 OvO http://codeforces.com/contest/837/problem/D 837D 解 DP, dp[i][j]代表已经选择 ...

  3. Codeforces 837F Prefix Sums

    Prefix Sums 在 n >= 4时候直接暴力. n <= 4的时候二分加矩阵快速幂去check #include<bits/stdc++.h> #define LL l ...

  4. CodeForces 1204E"Natasha, Sasha and the Prefix Sums"(动态规划 or 组合数学--卡特兰数的应用)

    传送门 •参考资料 [1]:CF1204E Natasha, Sasha and the Prefix Sums(动态规划+组合数) •题意 由 n 个 1 和 m 个 -1 组成的 $C_{n+m} ...

  5. 【题解】【数组】【Prefix Sums】【Codility】Genomic Range Query

    A non-empty zero-indexed string S is given. String S consists of N characters from the set of upper- ...

  6. 【题解】【数组】【Prefix Sums】【Codility】Passing Cars

    A non-empty zero-indexed array A consisting of N integers is given. The consecutive elements of arra ...

  7. CF1303G Sum of Prefix Sums

    点分治+李超树 因为题目要求的是树上所有路径,所以用点分治维护 因为在点分治的过程中相当于将树上经过当前$root$的一条路径分成了两段 那么先考虑如何计算两个数组合并后的答案 记数组$a$,$b$, ...

  8. Educational Codeforces Round 26 F. Prefix Sums 二分,组合数

    题目链接:http://codeforces.com/contest/837/problem/F 题意:如题QAQ 解法:参考题解博客:http://www.cnblogs.com/FxxL/p/72 ...

  9. Codeforces 1303G - Sum of Prefix Sums(李超线段树+点分治)

    Codeforces 题面传送门 & 洛谷题面传送门 个人感觉这题称不上毒瘤. 首先看到选一条路径之类的字眼可以轻松想到点分治,也就是我们每次取原树的重心 \(r\) 并将路径分为经过重心和不 ...

随机推荐

  1. 关于父类私有属性在子类构造函数中super调用的解释

    package test; public class Car { private int carMoney; //汽车租金 private String carName; //汽车名字 private ...

  2. L200

    Last week, I read a story about a 34-year-old British woman who is extremely afraid of metal forks.S ...

  3. L173

    Technical problems temporarily blocked some US and European users having access to their accounts an ...

  4. Winform开发常用控件之DataGridView的简单数据绑定——代码绑定DataSet、DataTable、IList、SqlDataReader

    前文介绍了Winform为DataGridView提供的数据自动绑定功能,下面介绍一下采用代码的数据绑定 1.用DataSet和DataTable为DataGridView提供数据源 先上代码 pri ...

  5. HDU 1847:Good Luck in CET-4 Everybody!(规律?博弈?)

    Good Luck in CET-4 Everybody! Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K ...

  6. Netty5.x 和3.x、4.x的区别及注意事项(官方翻译)

    Netty5.x 和3.x.4.x的区别及注意事项 (官方翻译) 本文档列出了Netty5新版本中值得注意变化和新特性列表.帮助你的应用更好的适应新的版本.   不像Netty3.x和4.x之间的变化 ...

  7. 定时器Timer&ScheduledThreadPoolExecutor

    定时器Timer&ScheduledThreadPoolExecutor /** * @ClassName: TimerTest * @author: daniel.zhao * @date: ...

  8. phpstorm 光标设置

    1.是否允许光标定位在行尾之后的任意位置Allow placement of caret after end of line 这个设置是上下换行的时候,光标可以定位在任意位置,只能通过方向键移动光标, ...

  9. 阿里云OSS linux使用备忘录

    ossutil config example: accessKeyId = "AccessKeyId"; accessKeySecret = "AccessKeySecr ...

  10. SpringIoc 和 工厂模式(反射实现)

    一.先演示 “简单工厂”: package org; interface Fruit { public void eat(); } class Apple implements Fruit { pub ...