CCF CSP Brush Question Record 36-202006-4 1246 (java)

 

[Topic description] The four numbers 1,2,4,6 have a magical property: if they are respectively taken to the power of the base 2, the obtained numbers are 2,4,16,64, which are still determined by these four numbers. Composed of numbers. We start with the number string 1, and every second of its digits will independently become a power of 2.

For example, in the first few seconds, the number string will turn into:

  1. 2
  2. 4
  3. 16
  4. 264
  5. 46416
  6. 166416264
  7. 264641626446416
  8. 46416641626446416166416264
  9. 166416264641626446416166416264264641626446416

Obviously, these number strings only contain the four numbers 1, 2, 4, and 6. Enter the integer n and the number string S. How many times does the number string of S appear in the nth second? Since the answer may be large, you only need to output the result of its modulo 998244353.

[Input format] Read data from standard input. Contains two lines, the first line is a number n, the second line is a string S.
[Output format] Output to standard output. There is only one line, containing an integer, which represents the desired answer.

【Sample 1 Input】

9
26

[Sample 1 output]

5

[Example 1 Explanation]
The number string in the 9th second is 166416264641626446416166416264264641626446416, of which 26 appears 5 times.

【Sample 2 Input】

2020
16

[Sample 2 output]

292008622

【data range】

This is the full decomposition method of the big guy I saw on the Internet, my own code is not posted, and the violence method is 28 points, which is too bad (´▽`)

The analysis of the boss is as follows:

  1. According to the change of the number, we can think based on the number; N = 10^9, the time complexity is log(n);
  2. Basically, there is a high probability that one digit will change to two. If the simulation method does not iterate for one second, the length of the string increases exponentially. Can pass the first 8 sets of tests. (You can write about it, get points quickly, and later, use it as a benchmark test case for the new algorithm)
  3. None of the digits can jump out of the set (1,2,4,6). If the location information is not considered, it is actually a linear recursion problem.

 

dp[i][1] -> dp[i+1][2]
dp[i][2] -> dp[i+1][4]
dp[i][4] -> dp[i+1][1], dp[i+1][6]
dp[i][6] -> dp[i+1][6], dp[i+1][4]

After discretizing 4 numbers into (0,1,2,3), it is easy to construct a transformation matrix

0 1 0 0 
0 0 1 0
1 0 0 1
0 0 1 1

Then there is a template for the fast power of the matrix.
Then we can pass all the test data of |S| = 1.

  1. If |S|> 1, my first feeling is by observing the 26 in the sample and the first few sets of numbers; I found that the number of 26 is equivalent to the number of 4 2 seconds ago, so all the numbers longer than 2 are First go back to the basic case of length 1 and then take the number of seconds subtracted to calculate.
    For example, 26, 9; equivalent to finding 4, 7

 

It is very important to observe this picture more

In this approach, an infinite loop occurs when processing a CASE with a digit of 2; for example, 46-"66-"46 forms a loop.
At the same time 64-"42-"61-"44-"62-"41-"64 is also a ring

So this idea does not work. Been stuck here for a long time.
In fact, it was observed that the CASE for 2 alone was solved and basically scored 96 points. So my idea is to build a state machine for 2-digit numbers.
such as

16-> 26,64
64-> 64,41,16
42-> 16,64

The problem with building a transformation matrix in this way is that it will be calculated repeatedly.
For example, the input is 264; it will become 41 1 time, 64 2 times, 61 1 time, 16 1 time
but the correct answer is 41 1 time, 64 1 time, 61 1 time, 16 1 time. The
reason is that 6 single numbers can be expanded. Into 2 digits. Therefore, the number generated when the first digit of 2 digits is 6 and the last digit is 6 will be repeated. At the same time 4 also has this problem.

Then I want to use the principle of tolerance and exclusion to solve the problem of repeated calculations, such as 4 and 6 and then the highest or lowest bit, just ignore it. But the first letter and last letter may have 4 and 6. This will miss the solution. Of course, if you do not use fast exponentiation, but linear iteration, this problem can be corrected in the recursive process. But 10^9 here requires that our recursive formula cannot have IF, it must be a direct transfer.

Finally, consider the column transformation equation by combining the 1-bit and 2-bit states. I found that it can not be repeated

  1. In the last 4 points, after writing the previous one, you can quickly find a 3-digit input. Backtracking to 2 digits is no loop. Overjoyed. Then I submitted and ran a TLE, because the first version of backtracking was written with violent backtracking. Each layer of recursion only processes 1 letter backtracking, enumerating every possibility and then pruning.
    Through further observation, I discovered a property that in the process of backtracking, in fact, the bifurcation is possible only in the first letter.
    For example, 4 can be obtained from the previous 2 or the previous 6. After this is confirmed, the rest is the only solution. If you can't get through, there will be no solution.
    So I optimized the writing, and finally AC.

  2. If the transformation matrix is ​​large, it can be generated by code. The core is to maintain the A state which can be transformed to which B state. Then the matrix rows represent FROM, can transform matrix column represents past TO, then mat[from][to]++
    a small partner can pull up look at the matrix S = 1 would understand.

The full score code of the boss is as follows:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        Main m = new Main();
        System.out.println(m.solve(sc.nextInt(), sc.next()));
    }
    int M = 998244353;
    int[] val2id = new int[67];
    int[] baseEle =   { 1,  2,     4,      6,     16,  26,  41,  42,  44,  46,  61,  62,  64,  66};
    int[][] convert = {
   
   {2},{4},{1,6,16},{6,4,64},{26},{46},{62},{64},{61},{66},{42},{44},{41},{46}};
    {
        Arrays.fill(val2id, -1);
        for (int i = 0; i < baseEle.length; i++) val2id[baseEle[i]] = i;
    }
    private int solve(int n, String s) {
        if (s.length() <= 2) return solveBase(val2id[Integer.parseInt(s)], n); // 96分
        else { // 4分
            int res = 0;
            for (int[] i : shorten(s, 0)) res = (res + solveBase(i[0], n - i[1])) % M;
            return res;
        }
    }
    
    private int solveBase(int id, int n) { // 矩阵快速幂
        int[][] init = new int[1][14]; init[0][0] = 1;
        int[][] mat = new int[14][14];
        for (int from  = 0; from < 14; from++) for (int to : convert[from]) mat[from][val2id[to]]++;
        while (n > 0) {
            if ((n & 1) == 1) init = mul(init, mat);
            mat = mul(mat, mat);
            n >>= 1;
        }
        return init[0][id];
    }

    private int[][] mul(int[][] a, int[][] b) { // 矩阵乘法
        int n = a.length, m = a[0].length, k = b[0].length;
        int[][] res = new int[n][k];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < k; j++)
                for (int q = 0; q < m; q++)
                    res[i][j] = (int) ((res[i][j] + (long) a[i][q] * b[q][j] % M) % M);
        return res;
    }

    int[] ones = {'4', 6, '6', 4}; // 2种忽略掉16的1,和64的6的特殊情况
    private List<int[]> shorten(String s, int dep) { // 回溯到上层;处理首字母,因为首字母可能有>1种情况
        if (s.length() <= 2) {
            int id = val2id[Integer.parseInt(s)];
            return id == -1 ? Collections.EMPTY_LIST : Arrays.asList(new int[]{id, dep});
        }
        List<int[]> res = new ArrayList<>();
        for (int i = 0; i < ones.length; i += 2)
            if (ones[i] == s.charAt(0)) res.addAll(shorten(ones[i+1] + postShorten(s, 1), dep + 1));
        res.addAll(shorten(postShorten(s, 0), dep + 1));
        return res;
    }
    String[] pos = {"2", "4", "/", "16", "/", "64"};
    private String postShorten(String s, int idx) { // 唯一可能性的回溯,找不到就返回一个非法字符代表无解
        StringBuilder sb = new StringBuilder();
        while (idx < s.length()) {
            int i = 0;
            for (; i < pos.length; i++) {
                if (s.startsWith(pos[i], idx) || (idx == s.length() - 1 && pos[i].charAt(0) == s.charAt(idx))) {
                    idx += pos[i].length();
                    sb.append(i + 1);
                    break;
                }
            }
            if (i == pos.length) return "0"; // invalid number
        }
        return sb.toString();
    }
}



Author: West dumplings
link: https: //www.jianshu.com/p/efc9e984eff0
Source: Jane books
are copyrighted by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

Guess you like

Origin blog.csdn.net/m0_37483148/article/details/108433622