Problem:
Score : 500 points

Problem Statement

Takahashi has a string S of length N consisting of digits from 0 through 9.

He loves the prime number P. He wants to know how many non-empty (contiguous) substrings of S - there are N \times (N + 1) / 2 of them - are divisible by P when regarded as integers written in base ten.

Here substrings starting with a 0 also count, and substrings originated from different positions in S are distinguished, even if they are equal as strings or integers.

Compute this count to help Takahashi.

Constraints

1 \leq N \leq 2 \times 10^5

S consists of digits.

|S| = N

2 \leq P \leq 10000

P is a prime number.

Input

Input is given from Standard Input in the following format:

N P
S

Output

Print the number of non-empty (contiguous) substrings of S that are divisible by P when regarded as an integer written in base ten.

Sample Input 1

4 3
3543

Sample Output 1

6

Here S = 3543. There are ten non-empty (contiguous) substrings of S:

3: divisible by 3.

35: not divisible by 3.

354: divisible by 3.

3543: divisible by 3.

5: not divisible by 3.

54: divisible by 3.

543: divisible by 3.

4: not divisible by 3.

43: not divisible by 3.

3: divisible by 3.

Six of these are divisible by 3, so print 6.

Sample Input 2

4 2
2020

Sample Output 2

10

Here S = 2020. There are ten non-empty (contiguous) substrings of S, all of which are divisible by 2, so print 10.

Note that substrings beginning with a 0 also count.

Sample Input 3

20 11
33883322005544116655

Sample Output 3

68

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Accepted)

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.PriorityQueue;


public class Main{
    public static void main(String[] args) throws Exception {
        FastScanner sc = new FastScanner();
        PrintWriter out = new PrintWriter(System.out);
        int N = sc.nextInt();
        int P = sc.nextInt();
        char[] S = sc.next().toCharArray();
        int[] modSum = new int[N+1];
        for(int i=N-1;i>=0;i--){
            modSum[i] = modSum[i+1] + (S[i]-'0') * (int) MyMath.repeatSquarePow(10, N-1-i, P);
            modSum[i] %= P;
        }
        int[] modCount = new int[P];
        for(int i=0;i<N+1;i++){
            modCount[modSum[i]]++;
        }
        long res = 0;
        if(P==2||P==5){
            for(int i=0;i<N;i++){
                if((S[i]-'0') % P == 0){
                    res += (i+1);
                }
            }
        } else {
            for(int i=0;i<P;i++){
                res += (long) modCount[i] * (modCount[i]-1) / 2;
            }
        }

        out.println(res);
        out.flush();
    }
}

class BinaryIndexedTree{
    int[] data;
    int[] tree;
    int n;

    public BinaryIndexedTree(int[] data){
        this.data = data;
        this.n = data.length;
        this.tree = new int[n+1];
        buildTree();
    }

    public void buildTree(){
        for(int i=0;i<n;i++){
            add(i+1, data[i]);
        }
    }

    public void add(int index, int value){
        for(int i=index; i<=n; i+=i & -i){
            tree[i] += value;
        }
    }

    public int sum(int from, int to){
        return sum(to) - sum(from-1);
    }

    public int sum(int index){
        int sum = 0;
        for(int i=index; i>0; i-=i & -i){
            sum += tree[i];
        }
        return sum;
    }
}

class nextPermutation{
    public static int[] swap(int data[], int left, int right)
    {

        // Swap the data
        int temp = data[left];
        data[left] = data[right];
        data[right] = temp;

        // Return the updated array
        return data;
    }

    // Function to reverse the sub-array
    // starting from left to the right
    // both inclusive
    public static int[] reverse(int data[], int left, int right)
    {

        // Reverse the sub-array
        while (left < right) {
            int temp = data[left];
            data[left++] = data[right];
            data[right--] = temp;
        }

        // Return the updated array
        return data;
    }

    // Function to find the next permutation
    // of the given integer array
    public static boolean findNextPermutation(int data[])
    {

        // If the given dataset is empty
        // or contains only one element
        // next_permutation is not possible
        if (data.length <= 1)
            return false;

        int last = data.length - 2;

        // find the longest non-increasing suffix
        // and find the pivot
        while (last >= 0) {
            if (data[last] < data[last + 1]) {
                break;
            }
            last--;
        }

        // If there is no increasing pair
        // there is no higher order permutation
        if (last < 0)
            return false;

        int nextGreater = data.length - 1;

        // Find the rightmost successor to the pivot
        for (int i = data.length - 1; i > last; i--) {
            if (data[i] > data[last]) {
                nextGreater = i;
                break;
            }
        }

        // Swap the successor and the pivot
        data = swap(data, nextGreater, last);

        // Reverse the suffix
        data = reverse(data, last + 1, data.length - 1);

        // Return true as the next_permutation is done
        return true;
    }
}

class graph{
    int[][] graph;
    ArrayList<Integer>[] edges;
    public graph(int[][] graph){
        this.graph = graph;

    }

    public long dijkstra(int s, int t){
        Integer[] dist = new Integer[graph.length];
        int[] prev = new int[graph.length];
        Arrays.fill(prev, -1);
        PriorityQueue<Integer> manage = new PriorityQueue<>((x,y) -> dist[x]-dist[y]);
        HashSet<Integer> searched = new HashSet<>();
        Arrays.fill(dist,Integer.MAX_VALUE);
        dist[s] = 0;
        searched.add(s);
        for(int i=0;i<graph.length;i++){
            if(graph[s][i]!=Integer.MAX_VALUE){
                dist[i] = graph[s][i];
                prev[i] = s;
                manage.add(i);
            }
        }
        while(manage.size()>0){
            int now = manage.poll();
            searched.add(now);
            for(int i=0;i<graph.length;i++){
                if(graph[now][i]!=Integer.MAX_VALUE){
                    if(!searched.contains(i)){
                        if(dist[i]==Integer.MAX_VALUE){
                            dist[i] = dist[now] + graph[now][i];
                            prev[i] = now;
                            manage.add(i);
                        }
                        else{
                            dist[i] = Math.min(dist[i],dist[now]+graph[now][i]);
                            if(dist[i]==dist[now]+graph[now][i]) prev[i] = now;
                            manage.remove(i);
                            manage.add(i);
                        }
                    }
                }
            }
            if(searched.contains(t)) break;
        }

        return dist[t];
    }

    public boolean judgeBipartitegraph(){
        int[] color = new int[graph.length];
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        HashSet<Integer> visited = new HashSet<>();
        queue.add(0);
        color[0] = 1;
        while(queue.size()>0){
            int now = queue.poll();
            visited.add(now);
            for(int j=0;j<graph[now].length;j++){
                if(visited.contains(j)) continue;
                if(graph[now][j]!=0){
                    if(color[j]==color[now]) return false;
                    queue.add(j);
                    if(color[j]==0) color[j] = -1*color[now];
                }
            }
        }
        return true;
    }

    public int[][] prim(){
        int[] mincost = new int[graph.length];
        boolean[] used = new boolean[graph.length];
        int[][] spanning = new int[graph.length][graph.length];
        for(int i=0;i<graph.length;i++){
            mincost[i] = Integer.MAX_VALUE;
            used[i] = false;
        }
        mincost[0] = 0;
        int totalCost = 0;
        while(true){
            int v = -1;
            for(int u=0;u<graph.length;u++){
                if(!used[u] && (v==-1||mincost[u]<mincost[v])){
                    v = u;
                }
            }
            if(v==-1) break;
            used[v] = true;
            totalCost += mincost[v];
            for(int u=0;u<graph.length;u++){
                if(graph[v][u]<mincost[u]){
                    mincost[u] = graph[v][u];
                }
            }
            for(int u=0;u<graph.length;u++){
                if(mincost[u]==graph[v][u]){
                    spanning[v][u] = 1;
                    spanning[u][v] = 1;
                    break;
                }
            }
        }
        return spanning;
        // return totalCost;
    }

    public int[][] wf(){
        int[][] dist = new int[graph.length][graph.length];
        dist = graph.clone();
        // for(int i=0;i<graph.length;i++){
        //     dist[i][i] = 0;
        // }
        for(int k=0;k<graph.length;k++){
            for(int i=0;i<graph.length;i++){
                for(int j=0;j<graph.length;j++){
                    if(dist[i][k]==Integer.MAX_VALUE || dist[k][j]==Integer.MAX_VALUE) continue;

                    dist[i][j] = Math.min(dist[i][j],dist[i][k]+dist[k][j]);

                }
            }
        }
        for(int i=0;i<graph.length;i++){
            if(dist[i][i]<0) return null;
        }
        return dist;
    }
}

class FastScanner {
    private final InputStream in = System.in;
    private final byte[] buffer = new byte[1024];
    private int ptr = 0;
    private int buflen = 0;
    private boolean hasNextByte() {
        if (ptr < buflen) {
            return true;
        }else{
            ptr = 0;
            try {
                buflen = in.read(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (buflen <= 0) {
                return false;
            }
        }
        return true;
    }
    private int readByte() { if (hasNextByte()) return buffer[ptr++]; else return -1;}
    private static boolean isPrintableChar(int c) { return 33 <= c && c <= 126;}
    public boolean hasNext() { while(hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++; return hasNextByte();}
    public String next() {
        if (!hasNext()) throw new NoSuchElementException();
        StringBuilder sb = new StringBuilder();
        int b = readByte();
        while(isPrintableChar(b)) {
            sb.appendCodePoint(b);
            b = readByte();
        }
        return sb.toString();
    }
    public long nextLong() {
        if (!hasNext()) throw new NoSuchElementException();
        long n = 0;
        boolean minus = false;
        int b = readByte();
        if (b == '-') {
            minus = true;
            b = readByte();
        }
        if (b < '0' || '9' < b) {
            throw new NumberFormatException();
        }
        while(true){
            if ('0' <= b && b <= '9') {
                n *= 10;
                n += b - '0';
            }else if(b == -1 || !isPrintableChar(b)){
                return minus ? -n : n;
            }else{
                throw new NumberFormatException();
            }
            b = readByte();
        }
    }
    public int nextInt() {
        long nl = nextLong();
        if (nl < Integer.MIN_VALUE || nl > Integer.MAX_VALUE) throw new NumberFormatException();
        return (int) nl;
    }
    public double nextDouble() { return Double.parseDouble(next());}
}

class UnionFindTree {
    int[] root;
    int[] rank;
    int[] size;
    public UnionFindTree(int n) {
        this.root = new int[n];
        this.rank = new int[n];
        this.size = new int[n];
        for(int i=0;i<n;i++){
            root[i] = i;
            size[i] = 1;
        }
    }

    public int find(int x){
        if(root[x]==x) return x;
        else{
            return root[x] = find(root[x]);
        }
    }

    public void unite(int x, int y){
        x = find(x);
        y = find(y);
        if(x==y) return;
        if(rank[x] < rank[y]){
            size[find(y)] = size[find(x)];
            root[x] = y;
        }
        else{
            size[find(x)] = size[find(y)];
            root[y] = x;
            if(rank[x]==rank[y]) rank[x]++;
        }
    }

    public boolean same(int x, int y){
        return find(x) == find(y);
    }

    public int size(int i){
        return this.size[find(i)];
    }
}

class MyMath {
    public static long gcm(long a, long b){

        if(a<b) return gcm(b, a);

        if(a%b==0 || b==0) return b;

        long remainder = a%b;
        return gcm(b, remainder);
    }

    public static long summation(long a, long b){
        return b*(b+1)/2-a*(a-1)/2;
    }

    public static long factorial(long n){

        if(n<=1) return 1;
        else return factorial(n-1)*n;
    }

    public static long binomial(int a, int b){
        if(a<b) return 0;
        long[][] table = new long[a+1][a+1];
        for(int i=0;i<a+1;i++){
            table[0][a] =  0;
            table[i][0] = 1;
            table[i][i] = 1;
        }
        for(int i=1;i<a+1;i++){
            for(int j=1;j<a+1;j++){
                if(i<j) table[i][j] = 0;
                else{
                    table[i][j] = table[i-1][j-1] + table[i-1][j];
                }
            }
        }
        return table[a][b];
    }

    public static long repeatSquarePow(int a, int n, int p){
        if(n==0) return 1;
        if(n==1) return a % p;
        if(n%2==1) return a * repeatSquarePow(a,n-1,p) % p;
        long tmp = repeatSquarePow(a, n/2, p) % p;
        return tmp*tmp % p;
    }

    public static long modinv(long a, long p){
        long b, x, u, q, abs_p, tmp;
        abs_p = Math.abs(p);
        b = p;
        x = 1;
        u = 0;
        while(b > 0){
            q = a / b;
            tmp = u;
            u = x - q*u;
            x = tmp;
            tmp = b;
            b = a - q*b;
            a = tmp;
        }
        return(x < 0) ? abs_p + x: x;
    }

    public static Long[] eratosthenes(int n){
        Long[] factor_table = new Long[n];
        for(long i=1;i<=n;i++){
            factor_table[(int) (i-1)] = i;
        }
        for(long i=1;i<=n;i++){
            if(factor_table[(int) (i-1)] == i){
                int j = 2;
                while(i * j <= n){
                    factor_table[(int) (i * j - 1)] = i;
                    j++;
                }
            }
        }

        return factor_table;
    }
}