#include "bits/stdc++.h"
#include <cmath>
using namespace std;

#define f first
#define s second

#define pb push_back
#define pp pop_back
#define mp make_pair

#define sz(x) (int)x.size()
#define sqr(x) ((x) * 1ll * (x))
#define all(x) x.begin(), x.end()
#define clr(a,v) memset(a, v, sizeof(a))

#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, l, r) for (int i = (l); i >= (r); i--)
#define reflex ios_base::sync_with_stdio(false)

#define nl '\n'
#define ioi exit(0);

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int N= (int)2e5 + 7;
const int inf = (int)1e9 + 7;
const int mod = (int)1e9 + 7;
//const int mod = 1000000007;
const ll linf = (ll)1e18 + 7;

const int dx[] = {-1, 0, 1, 0, 1, -1, -1, 1};
const int dy[] = {0, 1, 0, -1, 1, -1, 1, -1};
const int kx[] = {-1,1,2,2,1,-1,-2,-2};
const int ky[] = {2,2,1,-1,-2,-2,-1,1};

string conv(int a){stringstream ss;ss << a;string str = ss.str();return str;}
string conv(char a){stringstream ss;ss << a;string str = ss.str();return str;}
int conv(string a){stringstream geek(a); int x = 0; geek >> x;   return x;}

struct node 
{ 
   int minimum; 
   int maximum; 
}; 
  

int getMid(int s, int e) {  return s + (e -s)/2;  } 
  
struct node MaxMinUntill(struct node *st, int ss, int se, int qs, 
                         int qe, int index) 
{ 
    
    struct node tmp,left,right; 
    if (qs <= ss && qe >= se) 
        return st[index]; 
  
    
    if (se < qs || ss > qe) 
    { 
       tmp.minimum = INT_MAX; 
       tmp.maximum = INT_MIN; 
       return tmp; 
     } 
  
    int mid = getMid(ss, se); 
    left = MaxMinUntill(st, ss, mid, qs, qe, 2*index+1); 
    right = MaxMinUntill(st, mid+1, se, qs, qe, 2*index+2); 
    tmp.minimum = min(left.minimum, right.minimum); 
    tmp.maximum = max(left.maximum, right.maximum); 
    return tmp; 
} 
  
 
struct node MaxMin(struct node *st, int n, int qs, int qe) 
{ 
    struct node tmp; 
  
    if (qs < 0 || qe > n-1 || qs > qe) 
    { 
        printf("Invalid Input"); 
        tmp.minimum = INT_MIN; 
        tmp.minimum = INT_MAX; 
        return tmp; 
    } 
  
    return MaxMinUntill(st, 0, n-1, qs, qe, 0); 
} 
  
void constructSTUtil(int arr[], int ss, int se, struct node *st, int si) 
{ 
    if (ss == se) 
    { 
        st[si].minimum = arr[ss]; 
        st[si].maximum = arr[ss]; 
        return ; 
    } 
  
    int mid = getMid(ss, se); 
    constructSTUtil(arr, ss, mid, st, si*2+1); 
    constructSTUtil(arr, mid+1, se, st, si*2+2); 
  
    st[si].minimum = min(st[si*2+1].minimum, st[si*2+2].minimum); 
    st[si].maximum = max(st[si*2+1].maximum, st[si*2+2].maximum); 
} 
  
struct node *constructST(int arr[], int n) 
{ 
    int x = (int)(ceil(log2(n))); 
  
    int max_size = 2*(int)pow(2, x) - 1; 
  
    struct node *st = new struct node[max_size]; 
  
    constructSTUtil(arr, 0, n-1, st, 0); 
  
    return st; 
} 


class UnionFind {                                              // OOP style
private:
	vector<int> p, rank, setSize;
	// p = path toward the root of disjoint set; p[i] = i means it is root
	// rank = upper bound of the actual height of the tree; not reliable as accurate measure
	// setSize = size of each disjoint set

	int numSets;
public:
	UnionFind(int N) {
		setSize.assign(N, 1);
		numSets = N;
		rank.assign(N, 0);
		p.assign(N, 0);
		for (int i = 0; i < N; i++) p[i] = i;	// each belongs to its own set
	}

	int findSet(int i) {
		return (p[i] == i) ? i : (p[i] = findSet(p[i]));		// path compression: cut short of the path if possible
	}

	bool isSameSet(int i, int j) {
		return findSet(i) == findSet(j);
	}

	void unionSet(int i, int j) {
		if (!isSameSet(i, j)) {
			numSets--;
			int x = findSet(i), y = findSet(j);
			// rank is used to keep the tree short
			if (rank[x] > rank[y]) { p[y] = x; setSize[x] += setSize[y]; }
			else {
				p[x] = y; setSize[y] += setSize[x];
				if (rank[x] == rank[y]) rank[y]++;
			}
		}
	}

	int numDisjointSets() {		// # of disjoint sets
		return numSets;
	}

	int sizeOfSet(int i) {		// size of set
		return setSize[findSet(i)];
	}
};


// Returns length of the longest prefix 
// which is also suffix and the two do 
// not overlap. This function mainly is 
// copy computeLPSArray() of in below post 
// https://www.geeksforgeeks.org/searching-for-patterns-set-2-kmp-algorithm/ 
int longestPrefixSuffix(string& s) 
{ 
	int n = s.length(); 

	int lps[n]; 
	lps[0] = 0; // lps[0] is always 0 

	// length of the previous 
	// longest prefix suffix 
	int len = 0; 

	// the loop calculates lps[i] 
	// for i = 1 to n-1 
	int i = 1; 
	while (i < n) 
	{ 
		if (s[i] == s[len]) 
		{ 
			len++; 
			lps[i] = len; 
			i++; 
		} 
		else // (pat[i] != pat[len]) 
		{ 
			// This is tricky. Consider 
			// the example. AAACAAAA 
			// and i = 7. The idea is 
			// similar to search step. 
			if (len != 0) 
			{ 
				len = lps[len-1]; 

				// Also, note that we do 
				// not increment i here 
			} 
			else // if (len == 0) 
			{ 
				lps[i] = 0; 
				i++; 
			} 
		} 
	} 

	int res = lps[n-1]; 

	// Since we are looking for 
	// non overlapping parts. 
	return (res > n/2)? n/2 : res; 
} 

int v[N];
void insertSort(int n){
	for(int i = 1; i <= n; i++){
		int key = v[i];
		int j = i - 1;
		while(j >= 1 && key < v[j]){
			v[j+1] = v[j];
			j -= 1;
		}
		v[j+1] = key;
	} 
}

int ones(int n){
	if(n <= 9)
		return n;
	return n%10;
}

int tens(int n){
	if(n >= 10)
		return n/10;
	return 0;
}


int main(){
	int m, d; cin>> m >> d;
	int cont = 0;
	for(int i = 1; i <= m; i++){
		for(int j = 1; j <= d; j++){
			if(ones(j) >= 2 && tens(j) >= 2 && ones(j)*tens(j) == i){
				cont++;
			}
		}
	}
	cout<<cont;
	return 0;
}



Measured memory_bytes: 256