#include<bits/stdc++.h>
#include<unordered_map>

#define ll long long
#define pii pair<int,int >
#define pll pair<long long ,long long >
#define pbb pair<bool,bool>
#define plll pair<long long,pair<long long ,long long > >
#define sefi second.first
#define sese second.second
#define fi first
#define se second
#define ld long double
#define pb push_back
#define mp make_pair
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()
#define sz(x) x.size()
#define sqr(x) x*x
#define cont continue
#define endln '\n'
#define NO cout<<"NO"<<endln
#define YES cout<<"YES"<<endln
//#define int long long

const long long  md = 998244353;
const int Inf = 1e9;
const ll Inf64 = 1e18;
const ll MaxN = 260;
const ll LogN = 20;
const ll MaxM = 1e10;
const ld eps = 1e-7;
const ll dx[4] = { 0,1,0,-1 };
const ll dy[4] = { 1,0,-1,0 };
const ll ddx[4] = { 1,1,-1,-1 };
const ll ddy[4] = { 1,-1,1,-1 };
const ld Pi = acos(-1);

using namespace std;

inline ll Mod(ll &a, ll m = md) {
	while (a > m)
		a -= m;
	return a;
}

inline ll gcd(ll a, ll b) {
	while (a) {
		b %= a;
		swap(a, b);
	}
	return b;
}

inline ll gcdex(ll a, ll mod = md) {
	ll g = mod, r = a, x = 0, y = 1;
	while (r != 0) {
		ll q = g / r;
		g %= r;
		swap(g, r);
		x -= q * y;
		swap(x, y);
	}
	return x < 0 ? x + mod : x;
}

inline ll binpow(ll a, ll n, ll m = md) {
	ll res = 1;
	while (n) {
		if (n & 1) {
			res *= a;
			res %= m;
		}
		a *= a;
		a %= m;
		n >>= 1;
	}
	return res % md;
}

// ///////
/// ////
// ///////

ll Cnt[MaxN][MaxN][26];
ll Hsh[26];
ll K = 251;
char Mx[MaxN][MaxN];
signed main() {
	ios_base::sync_with_stdio(0);
	//ifstream cin("34.in");
	//ofstream cout("34.out");
	cin.tie(0);
	cout.tie(0);
	//cerr.tie(0);
	//cout << fixed, cout.precision(0);
	// //

	Hsh[0] = 1;
	for (int i = 1;i < 26;i++) {
		Hsh[i]  = Hsh[i - 1] * K;
		Hsh[i] %= md;
	}

	ll N, M;
	cin >> N >> M;
	for (int i = 1;i < N+1;i++) {
		for (int j = 1;j < M+1;j++) {
			cin >> Mx[i][j];
			for (int k = j;k < M+1;k++) {
				Cnt[i][k][Mx[i][j] - 'a'] ++;
			}
		}
	}
	ll Ans = 0;
	for (int i = 1;i <= M;i++) {
		for (int j = i;j <= M;j++) {
			vector<ll > Mt(N+1,-1);
			for (int k = 1;k <= N;k++) {
				bool f = 0;
				ll w = 0;
				for (int t = 0;t < 26;t++) {
					if ((Cnt[k][j][t] - Cnt[k][i - 1][t] & 1) == 1 && f) {
						w = -1;
						break;
					}
					if ((Cnt[k][j][t] - Cnt[k][i - 1][t] & 1) == 1 && !f) {
						f = 1;
					}
					w += Hsh[t] * (Cnt[k][j][t] - Cnt[k][i - 1][t]);
					w %= md;
				}
				Mt[k] = w;
			}

			vector<ll> d1(N+1);
			for (ll i = 1, l = 1, r = 0; i <= N; i++) {
				int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);
				if (Mt[i] == -1) k = 0;
				while (0 < i - k && i + k <=N && Mt[i - k] == Mt[i + k] && Mt[i+k] != -1) {
					k++;
				}
				d1[i] = k--;
				if (i + k > r) {
					l = i - k;
					r = i + k;
				}
				Ans += d1[i];
			}
			d1.assign(N + 1, 0);
			for (ll i = 1, l = 0, r = -1; i <= N; i++) {
				int k = (i > r) ? 0 : min(d1[l + r - i + 1], r - i + 1);
				if (Mt[i] == -1) k = 0;
				while (0 < i - k - 1 && i + k <= N && Mt[i - k - 1] == Mt[i + k] && Mt[i + k] != -1) {
					k++;
				}
				d1[i] = k--;
				if (i + k > r) {
					l = i - k - 1;
					r = i + k;
				}
				Ans += d1[i];
			}

		}
	}
	cout << Ans;
	// //////////////
	cerr << '\n' << "Time execute: " << clock() / (double)CLOCKS_PER_SEC << " sec" << '\n';

#ifdef LOCAL
	system("pause");
#endif

	return 0;

}
/*
2 3 2
49529 164864 8367
168383 521372 25974

*/


#include<bits/stdc++.h>
#include<unordered_map>

#define ll long long
#define pii pair<int,int >
#define pll pair<long long ,long long >
#define pbb pair<bool,bool>
#define plll pair<long long,pair<long long ,long long > >
#define sefi second.first
#define sese second.second
#define fi first
#define se second
#define ld long double
#define pb push_back
#define mp make_pair
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()
#define sz(x) x.size()
#define sqr(x) x*x
#define cont continue
#define endln '\n'
#define NO cout<<"NO"<<endln
#define YES cout<<"YES"<<endln
//#define int long long

const long long  md = 998244353;
const int Inf = 1e9;
const ll Inf64 = 1e18;
const ll MaxN = 260;
const ll LogN = 20;
const ll MaxM = 1e10;
const ld eps = 1e-7;
const ll dx[4] = { 0,1,0,-1 };
const ll dy[4] = { 1,0,-1,0 };
const ll ddx[4] = { 1,1,-1,-1 };
const ll ddy[4] = { 1,-1,1,-1 };
const ld Pi = acos(-1);

using namespace std;

inline ll Mod(ll &a, ll m = md) {
	while (a > m)
		a -= m;
	return a;
}

inline ll gcd(ll a, ll b) {
	while (a) {
		b %= a;
		swap(a, b);
	}
	return b;
}

inline ll gcdex(ll a, ll mod = md) {
	ll g = mod, r = a, x = 0, y = 1;
	while (r != 0) {
		ll q = g / r;
		g %= r;
		swap(g, r);
		x -= q * y;
		swap(x, y);
	}
	return x < 0 ? x + mod : x;
}

inline ll binpow(ll a, ll n, ll m = md) {
	ll res = 1;
	while (n) {
		if (n & 1) {
			res *= a;
			res %= m;
		}
		a *= a;
		a %= m;
		n >>= 1;
	}
	return res % md;
}

// ///////
/// ////
// ///////

bitset<MaxN> used[MaxN][MaxN];
ll Ht[MaxN][MaxN];
ll Hsh[26];
ll K = 251;
char Mx[MaxN][MaxN];
signed main() {
	ios_base::sync_with_stdio(0);
	//ifstream cin("34.in");
	//ofstream cout("34.out");
	cin.tie(0);
	cout.tie(0);
	//cerr.tie(0);
	//cout << fixed, cout.precision(0);
	// //

	Hsh[0] = 1;
	for (int i = 1;i < 26;i++) {
		Hsh[i]  = Hsh[i - 1] * K;
		Hsh[i] %= md;
	}

	ll N, M;
	cin >> N >> M;
	for (int i = 1;i < N+1;i++) {
		for (int j = 1;j < M+1;j++) {
			cin >> Mx[i][j];
			Ht[i][j] = Ht[i][j - 1] + Hsh[Mx[i][j] - 'a'];
			Ht[i][j] %= md;
			vector<int > Sv(26);
				ll t = 0;

			for (int k = j;k > 0;k--) {
				if (Sv[Mx[i][k] - 'a'] % 2 == 0) {
					t++;
					Sv[Mx[i][k] - 'a']++;
				}
				else {
					t--;
					Sv[Mx[i][k] - 'a']++;
				}

				if (t <= 1) {
					used[i][k][j] = 1;
				}
			}
		}
	}
	ll Ans = 0;
	for (int i = 1;i <= M;i++) {
		for (int j = i;j <= M;j++) {
			vector<ll > Mt(N+1,-1);
			for (int k = 1;k <= N;k++) {

				ll w = 0;
				if (!used[k][i][j])w = -1;
				else {
					w = Ht[k][j] - Ht[k][i - 1];
					if (w < 0)w += md;
				}

				Mt[k] = w;
			}

			vector<ll> d1(N+1);
			for (ll i = 1, l = 1, r = 0; i <= N; i++) {
				int k = (i > r) ? 1 : min(d1[l + r - i], r - i + 1);
				if (Mt[i] == -1) k = 0;
				while (0 < i - k && i + k <=N && Mt[i - k] == Mt[i + k] && Mt[i+k] != -1) {
					k++;
				}
				d1[i] = k--;
				if (i + k > r) {
					l = i - k;
					r = i + k;
				}
				Ans += d1[i];
			}
			d1.assign(N + 1, 0);
			for (ll i = 1, l = 0, r = -1; i <= N; i++) {
				int k = (i > r) ? 0 : min(d1[l + r - i + 1], r - i + 1);
				if (Mt[i] == -1) k = 0;
				while (0 < i - k - 1 && i + k <= N && Mt[i - k - 1] == Mt[i + k] && Mt[i + k] != -1) {
					k++;
				}
				d1[i] = k--;
				if (i + k > r) {
					l = i - k - 1;
					r = i + k;
				}
				Ans += d1[i];
			}

		}
	}
	cout << Ans;
	// //////////////
	cerr << '\n' << "Time execute: " << clock() / (double)CLOCKS_PER_SEC << " sec" << '\n';

#ifdef LOCAL
	system("pause");
#endif

	return 0;

}
/*
2 3 2
49529 164864 8367
168383 521372 25974

*/


#include "bits/stdc++.h"
#define MAXN 100009
#define INF 1000000007
#define mp(x,y) make_pair(x,y)
#define all(v) v.begin(),v.end()
#define pb(x) push_back(x)
#define wr cout<<"----------------"<<endl;
#define ppb() pop_back()
#define tr(ii,c) for(__typeof((c).begin()) ii=(c).begin();ii!=(c).end();ii++)
#define ff first
#define ss second
#define my_little_dodge 46
#define debug(x)  cerr<< #x <<" = "<< x<<endl;
using namespace std;

typedef long long ll;
typedef pair<int,int> PII;
template<class T>bool umin(T& a,T b){if(a>b){a=b;return 1;}return 0;}
template<class T>bool umax(T& a,T b){if(a<b){a=b;return 1;}return 0;}
vector<int>d1,d2;
const int N=255;
int cnt[26][N],n,m;
PII arr[N];
char s[N][N];
void solve(){d1.clear();d1.resize(n);
  int l=0,r=-1;
  for(int i=0;i<n;i++){
    int k=(i>r?0:min(d1[l+r-i],r-i))+1;
    while(i+k<n and i-k>=0 and arr[i+k]==arr[i-k])
      k++;
    d1[i]=k--;
    if(i+k>r)
      l=i-k,r=i+k;
  }d2.clear();d2.resize(n);
  l=0,r=-1;
  for(int i=0;i<n;i++){
    int k=(i>r?0:min(d2[l+r-i+1], r-i+1))+1;
    while(i+k-1<n and i-k>=0 and arr[i+k-1]==arr[i-k])
      k++;
    d2[i]=--k;
    if (i+k-1>r)
      l=i-k,r=i+k-1;
  }
}
bool bad[N];
int wp[26],pw[26];
int dp[N],odd[N];
int main(){
   // freopen("file.in", "r", stdin);
  scanf("%d%d",&n,&m);
  for(int i=0;i<n;i++)
    scanf("%s",s[i]);
  pw[0]=wp[0]=1;
  for(int i=1;i<26;i++){
    pw[i]=(pw[i-1]*701LL)%INF;
    wp[i]=(wp[i-1]*703LL)%INF;
  }
  ll ans=0;
  for(int i=0;i<m;i++){
    memset(cnt,0,sizeof cnt);
    memset(arr,0,sizeof arr);
    memset(odd,0,sizeof odd);
    for(int j=i;j<m;j++){
      int last=-1;
      for(int k=0;k<n;k++){
        cnt[s[k][j]-'a'][k]++;
        arr[k].ff+=pw[s[k][j]-'a'];
        if(arr[k].ff>=INF)
          arr[k].ff-=INF;
        arr[k].ss+=wp[s[k][j]-'a'];
        if(arr[k].ss>=INF)
          arr[k].ss-=INF;
        odd[k]+=(cnt[s[k][j]-'a'][k]&1)*2-1;
        if(odd[k]>=2)
          bad[k]=1,last=k;
        else
          bad[k]=0;
        dp[k]=last;
      }solve();last=n;
      for(int k=n-1;k>=0;k--){
        if(bad[k]){
          last=k;
          continue;
        }
        ans+=min(d1[k],min(k-dp[k],last-k));
        if(d2[k] and dp[k]!=k-1)
          ans+=min(d2[k],min(k-1-dp[k-1],last-k));
      }
    }
  }
  printf("%lld\n",ans);
  return 0;
}


#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4")

#define fi first
#define se second

const int N = 250;
const int Q = 1000100;
const long long mod = 1e9 + 7;

using namespace std;

int n;
int m;
int col[N];
char c[N][N];
bitset < N > f[N + N + 2];
bitset < N > pol[N][N];
bitset < N > mask[N];

int main()
{
        //freopen("input.txt","r",stdin);
        //freopen("output.txt","w",stdout);
        //freopen("nodes.in","r",stdin);
        ios_base::sync_with_stdio(0);

        cin >> n >> m;
        for(int i = 0; i < n; i++){
                for(int j = 0; j < m; j++){
                        cin >> c[i][j];
                        c[i][j] -= 'a';
                }
        }
        for(int i = 0; i < n; i++){
                for(int j = 0; j < m; j++){
                        for(int h = 0; h < 26; h++){
                                col[h] = 0;
                        }
                        int bad = 0;
                        for(int h = j; h < m; h++){
                                bad -= col[c[i][h]] & 1;
                                col[c[i][h]]++;
                                bad += col[c[i][h]] & 1;
                                if(bad < 2){
                                        pol[i][j][h] = 1;
                                }
                        }
                }
        }
        long long ans = 0;
        for(int i = 0; i < n; i++){
                for(pair < int, int > p: {make_pair(i, i), make_pair(i - 1, i)}){
                        for(int j = 0; j < m; j++){
                                mask[j].reset();
                                mask[j].flip();
                        }
                        int l = p.fi, r = p.se;
                        while(l >= 0 && r < n){
                                for(int j = 0; j < m; j++){
                                        mask[j] &= pol[l][j];
                                }
                                for(int g = 0; g < 26; g++){
                                        int s = 0;
                                        for(int j = m - 1; j >= 0; j--){
                                                f[s + N][j] = 1;
                                                s += c[l][j] == g;
                                                s -= c[r][j] == g;
                                                mask[j] &= f[s + N];
                                        }
                                        s = 0;
                                        for(int j = m - 1; j >= 0; j--){
                                                f[s + N][j] = 0;
                                                s += c[l][j] == g;
                                                s -= c[r][j] == g;
                                        }
                                }
                                for(int j = 0; j < m; j++){
                                        ans += mask[j].count();
                                }
                                l--;
                                r++;
                        }
                }
        }
        cout << ans << "\n";
}


#include <bits/stdc++.h>
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4")

#define fi first
#define se second

const int N = 250;
const int Q = 1000100;
const long long mod = 1e9 + 7;

using namespace std;

int n;
int m;
int col[N];
char c[N][N];
bitset < N > f[N + N + 2];
bitset < N > good[N][N][N];
bitset < N > pol[N][N];

int main()
{
        //freopen("input.txt","r",stdin);
        //freopen("output.txt","w",stdout);
        //freopen("nodes.in","r",stdin);
        ios_base::sync_with_stdio(0);

        cin >> n >> m;
        for(int i = 0; i < n; i++){
                for(int j = 0; j < m; j++){
                        cin >> c[i][j];
                        c[i][j] -= 'a';
                }
        }
        for(int i = 0; i < n; i++){
                for(int j = i + 1; j < n; j++){
                        for(int h = 0; h < m; h++){
                                good[i][j][h].flip();
                        }
                }
        }
        for(int g = 0; g < 26; g++){
                for(int i = 0; i < n; i++){
                        for(int j = i + 1; j < n; j++){
                                int s = 0;
                                for(int h = m - 1; h >= 0; h--){
                                        f[s + N][h] = 1;
                                        s += c[i][h] == g;
                                        s -= c[j][h] == g;
                                        good[i][j][h] &= f[s + N];
                                }
                                s = 0;
                                for(int h = m - 1; h >= 0; h--){
                                        s += c[i][h] == g;
                                        s -= c[j][h] == g;
                                        f[s + N][h] = 0;
                                }
                        }
                }
        }
        for(int i = 0; i < n; i++){
                for(int j = 0; j < m; j++){
                        for(int h = 0; h < 26; h++){
                                col[h] = 0;
                        }
                        int bad = 0;
                        for(int h = j; h < m; h++){
                                bad -= col[c[i][h]] & 1;
                                col[c[i][h]]++;
                                bad += col[c[i][h]] & 1;
                                if(bad < 2){
                                        pol[i][j][h] = 1;
                                }
                        }
                }
        }
        bitset < N > mask;
        long long ans = 0;
        for(int i = 0; i < n; i++){
                for(int j = 0; j < m; j++){
                        int l = i, r = i;
                        mask = pol[i][j];
                        ans += mask.count();
                        while(l > 0 && r + 1 < n){
                                l--;
                                r++;
                                mask &= pol[l][j];
                                mask &= good[l][r][j];
                                ans += mask.count();
                        }
                        if(i == 0){
                                continue;
                        }
                        mask = pol[i - 1][j] & good[i - 1][i][j];
                        l = i - 1, r = i;
                        ans += mask.count();
                        while(l > 0 && r + 1 < n){
                                l--;
                                r++;
                                mask &= pol[l][j];
                                mask &= good[l][r][j];
                                ans += mask.count();
                        }
                }
        }
        cout << ans << "\n";
}


#include "bits/stdc++.h"
#define MAXN 100009
#define INF 1000000007
#define mp(x,y) make_pair(x,y)
#define all(v) v.begin(),v.end()
#define pb(x) push_back(x)
#define wr cout<<"----------------"<<endl;
#define ppb() pop_back()
#define tr(ii,c) for(__typeof((c).begin()) ii=(c).begin();ii!=(c).end();ii++)
#define ff first
#define ss second
#define my_little_dodge 46
#define debug(x)  cerr<< #x <<" = "<< x<<endl;
using namespace std;

typedef long long ll;
typedef pair<int,int> PII;
template<class T>bool umin(T& a,T b){if(a>b){a=b;return 1;}return 0;}
template<class T>bool umax(T& a,T b){if(a<b){a=b;return 1;}return 0;}
vector<int>d1,d2;
const int N=255;
int cnt[26][N],arr[N],n,m;
char s[N][N];
void solve(){d1.clear();d1.resize(n);
  int l=0,r=-1;
  for(int i=0;i<n;i++){
    int k=(i>r?0:min(d1[l+r-i],r-i))+1;
    while(i+k<n and i-k>=0 and arr[i+k]==arr[i-k])
      k++;
    d1[i]=k--;
    if(i+k>r)
      l=i-k,r=i+k;
  }d2.clear();d2.resize(n);
  l=0,r=-1;
  for(int i=0;i<n;i++){
    int k=(i>r?0:min(d2[l+r-i+1], r-i+1))+1;
    while(i+k-1<n and i-k>=0 and arr[i+k-1]==arr[i-k])
      k++;
    d2[i]=--k;
    if (i+k-1>r)
      l=i-k,r=i+k-1;
  }
}
bool bad[N];
int pw[26],dp[N],odd[N];
int main(){
   // freopen("file.in", "r", stdin);
  scanf("%d%d",&n,&m);
  for(int i=0;i<n;i++)
    scanf("%s",s[i]);
  pw[0]=1;
  for(int i=1;i<26;i++)
    pw[i]=(pw[i-1]*701LL)%INF;
  ll ans=0;
  for(int i=0;i<m;i++){
    memset(cnt,0,sizeof cnt);
    memset(arr,0,sizeof arr);
    memset(odd,0,sizeof odd);
    for(int j=i;j<m;j++){
      int last=-1;
      for(int k=0;k<n;k++){
        cnt[s[k][j]-'a'][k]++;
        arr[k]+=pw[s[k][j]-'a'];
        if(arr[k]>=INF)
          arr[k]-=INF;
        odd[k]+=(cnt[s[k][j]-'a'][k]&1)*2-1;
        if(odd[k]>=2)
          bad[k]=1,last=k;
        else
          bad[k]=0;
        dp[k]=last;
      }solve();last=n;
      for(int k=n-1;k>=0;k--){
        if(bad[k]){
          last=k;
          continue;
        }
        ans+=min(d1[k],min(k-dp[k],last-k));
        if(d2[k] and dp[k]!=k-1)
          ans+=min(d2[k],min(k-1-dp[k-1],last-k));
      }
    }
  }
  printf("%lld\n",ans);
  return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

/*
 * Given a string of length N, returns a vector "pal" of 2N-1 elements
 * For even i, pal[i] = the length of the largest palindrome centered
 *                      at index i/2 of the input string
 * For odd i,  pal[i] = the length of the largest palindrome centered
 *                      between indices i/2 and i/2+1 of the input string
 */
template<typename T> vi manacher(const vector<T>& str) {
    vi pal(max(2 * int(str.size()) - 1, 0));

    auto match = [&str, &pal](int i, int l) {
        if (i-l < -1 || i+l > pal.size()) return false;
        return (i-l)&1 || str[(i-l)/2] == str[(i+l)/2];
    };

    for (int c = 0, end = -1, ref; c < pal.size(); c++) {
        if (end > c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (match(c, pal[c] + 1))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    MN base = 256 + rng() % 256;
    vector<MN> pows({1});
    for (int e = 1; e < 256; e++)
        pows.push_back(pows.back() * base);

    ll ans = 0;
    vector<MN> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            vi pals = manacher(hashes);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
        vvi rct(N, vi(26));
        vector<MN> rh(N);
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rh[r] -= rct[r][add] * pows[add];
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;
                rh[r] += rct[r][add] * pows[add];

                if (odd[r] <= 1) {
                    hashes.push_back(rh[r]);
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

template<typename T> vi manacher(const vector<T>& str) {
    if (str.empty()) return {};

    auto eq = [&str](int i, int j) {
        return (i&1) || (str[i/2] == str[j/2]);
    };
    auto exp = [&str, &eq](int i, int l) {
        if (i-l < 0 || i+l >= 2 * str.size() - 1) return false;
        return eq(i-l, i+l);
    };

    vi pal(str.size() * 2 - 1, 0);
    for (int c = 0, ref = -1, end = -1; c < pal.size(); c++) {
        if (end > c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (exp(c, pal[c]))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    rabin_karp<MN, 3, int> rk(256);

    ll ans = 0;
    vector<array<MN, 3>> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            for (auto h : hashes)
                WATCHC(h);
            vi pals = manacher(hashes);
            WATCHC(pals);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
       vvi rct(N, vi(26));
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;

                if (odd[r] <= 1) {
                    hashes.push_back(rk.hash(rct[r])(0, 26));
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

template<typename T> vi manacher(const vector<T>& str) {
    if (str.empty()) return {};

    auto eq = [&str](int i, int j) {
        return (i&1) || (str[i/2] == str[j/2]);
    };
    auto exp = [&str, &eq](int i, int l) {
        if (i-l < 0 || i+l >= 2 * str.size() - 1) return false;
        return eq(i-l, i+l);
    };

    vi pal(str.size() * 2 - 1, 0);
    for (int c = 0, ref = -1, end = -1; c < pal.size(); c++) {
        if (end > c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (exp(c, pal[c]))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    rabin_karp<MN, 3, int> rk(256);

    ll ans = 0;
    vector<array<MN, 3>> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            vi pals = manacher(hashes);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
       vvi rct(N, vi(26));
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;

                if (odd[r] <= 1) {
                    hashes.push_back(rk.hash(rct[r])(0, 26));
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

/*
 * Given a string of length N, returns an array "pal" of 2N-1 integers.
 * For even i, pal[i] = length of the longest odd-length palindrome
 *                      centered around index i/2.
 * For odd i, pal[i] = length of the longest even-length palindrome
 *                     centered between indices i/2 and i/2+1.
 */
template<typename T> vi manacher(const vector<T>& str) {
    vi pal(max(2 * int(str.size()) - 1, 0));

    auto match = [&str, &pal](int i, int l) {
        if (i-l < 0 || i+l >= pal.size()) return false;
        return (i-l)&1 || str[(i-l)/2] == str[(i+l)/2];
    };
    for (int c = 0, end = -1, ref; c < pal.size(); c++) {
        if (end > c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (match(c, pal[c]))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }

    for (int i = 0; i < pal.size(); i++)
        pal[i] = 2 * ((i + pal[i] - 1)/2 - i/2) + !(i&1);
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    MN base = 256 + rng() % 256;
    vector<MN> pows({1});
    for (int e = 1; e < 256; e++)
        pows.push_back(pows.back() * base);

    ll ans = 0;
    vector<MN> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            vi pals = manacher(hashes);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
        vvi rct(N, vi(26));
        vector<MN> rh(N);
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rh[r] -= rct[r][add] * pows[add];
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;
                rh[r] += rct[r][add] * pows[add];

                if (odd[r] <= 1) {
                    hashes.push_back(rh[r]);
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

template<typename T> vi manacher(const vector<T>& str) {
    vi pal(max(2 * int(str.size()) - 1, 0));
    auto match = [&str, &pal](int i, int l) {
        if (i-l < 0 || i+l >= pal.size()) return false;
        return (i-l)&1 || str[(i-l)/2] == str[(i+l)/2];
    };

    for (int c = 0, end = -1, ref; c < pal.size(); c++) {
        if (end > c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (match(c, pal[c]))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    MN base = 256 + rng() % 256;
    vector<MN> pows({1});
    for (int e = 1; e < 256; e++)
        pows.push_back(pows.back() * base);

    ll ans = 0;
    vector<MN> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            vi pals = manacher(hashes);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
        vvi rct(N, vi(26));
        vector<MN> rh(N);
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rh[r] -= rct[r][add] * pows[add];
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;
                rh[r] += rct[r][add] * pows[add];

                if (odd[r] <= 1) {
                    hashes.push_back(rh[r]);
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

/*
 * Given a string of length N, returns a vector "pal" of 2N-1 elements
 * For even i, pal[i] = the length of the largest palindrome centered
 *                      at index i/2 of the input string
 * For odd i,  pal[i] = the length of the largest palindrome centered
 *                      between indices i/2 and i/2+1 of the input string
 */
template<typename T> vi manacher(const vector<T>& str) {
    vi pal(max(2 * int(str.size()) - 1, 0));

    auto match = [&str, &pal](int i, int l) {
        if (i-l < -1 || i+l > pal.size()) return false;
        return (i-l)&1 || str[(i-l)/2] == str[(i+l)/2];
    };

    for (int c = 0, end = -1, ref; c < pal.size(); c++) {
        if (end > c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (match(c, pal[c] + 1))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }

    for (int i = 0; i < pal.size(); i += 2)
        pal[i]++;
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    MN base = 256 + rng() % 256;
    vector<MN> pows({1});
    for (int e = 1; e < 256; e++)
        pows.push_back(pows.back() * base);

    ll ans = 0;
    vector<MN> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            vi pals = manacher(hashes);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
        vvi rct(N, vi(26));
        vector<MN> rh(N);
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rh[r] -= rct[r][add] * pows[add];
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;
                rh[r] += rct[r][add] * pows[add];

                if (odd[r] <= 1) {
                    hashes.push_back(rh[r]);
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

/*
 * Given a string of length N, returns an array "pal" of 2N-1 integers.
 * For even i, pal[i] = length of the longest odd-length palindrome
 *                      centered around index i/2.
 * For odd i, pal[i] = length of the longest even-length palindrome
 *                     centered between indices i/2 and i/2+1.
 */
template<typename T> vi manacher(const vector<T>& str) {
    vi pal(max(2 * int(str.size()) - 1, 0));

    auto match = [&str, &pal](int i, int l) {
        if (i-l < 0 || i+l >= pal.size()) return false;
        return (i-l)&1 || str[(i-l)/2] == str[(i+l)/2];
    };
    for (int c = 0, end = -1, ref; c < pal.size(); c++) {
        if (end > c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (match(c, pal[c]))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }

    for (int i = 0; i < pal.size(); i += 2)
        pal[i]++;
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    MN base = 256 + rng() % 256;
    vector<MN> pows({1});
    for (int e = 1; e < 256; e++)
        pows.push_back(pows.back() * base);

    ll ans = 0;
    vector<MN> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            vi pals = manacher(hashes);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
        vvi rct(N, vi(26));
        vector<MN> rh(N);
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rh[r] -= rct[r][add] * pows[add];
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;
                rh[r] += rct[r][add] * pows[add];

                if (odd[r] <= 1) {
                    hashes.push_back(rh[r]);
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


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

#define TRACE(x)
#define WATCH(x) TRACE(cout << #x" = " << x << endl)
#define WATCHR(a, b) TRACE(for (auto it=a; it!=b;) cout << *(it++) << " "; cout << endl)
#define WATCHC(V) TRACE({cout << #V" = "; WATCHR(V.begin(), V.end());})

#define all(x) (x).begin(), (x).end()

using ll = long long;
using vi = vector<int>;
using vvi = vector<vi>;
using vll = vector<ll>;
using vvll = vector<vll>;
using vb = vector<bool>;
using vs = vector<string>;

mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template<int MOD> struct modnum {
    int v;
    modnum() : v(0) {}
    modnum(ll _v) : v(_v % MOD) { if (v < 0) v += MOD; }
    explicit operator int() const { return v; }
    friend ostream& operator << (ostream& o, const modnum& n) { return o << n.v; }
    friend istream& operator << (istream& i, modnum& n) { ll v; i >> v; n = modnum(v); return i; }

    friend bool operator == (const modnum& a, const modnum& b) { return a.v == b.v; }
    friend bool operator != (const modnum& a, const modnum& b) { return a.v != b.v; }

    modnum& operator += (const modnum& o) { v += o.v; if (v >= MOD) v -= MOD; return *this; }
    modnum& operator -= (const modnum& o) { v -= o.v; if (v < 0) v += MOD; return *this; }
    modnum& operator *= (const modnum& o) { v = int(ll(v) * ll(o.v) % MOD); return *this; }
    modnum operator - () { modnum res; if (v) res.v = MOD - v; return res; }
    friend modnum operator + (const modnum& a, const modnum& b) { return modnum(a) += b; }
    friend modnum operator - (const modnum& a, const modnum& b) { return modnum(a) -= b; }
    friend modnum operator * (const modnum& a, const modnum& b) { return modnum(a) *= b; }

    modnum pow(int e) const {
        if (e == 0) return 1;
        if (e & 1) return *this * this->pow(e-1);
        return (*this * *this).pow(e/2);
    }

    /* requires prime MOD */
    modnum& operator /= (const modnum& o) { return (*this) *= o.pow(MOD - 2); }
    friend modnum operator / (const modnum& a, const modnum& b) { return modnum(a) /= modnum(b); }
};

template<typename H, int B, typename V> struct rabin_karp {
    array<H, B> bases;
    vector<H> pows;

    rabin_karp(int maxv) {
        for (int i = 0; i < B; i++)
            bases[i] = H(maxv + rng() % maxv);
        pows.resize(B, H(1));
    }
    H pow(int i, int e) {
        while (pows.size() <= B * e + i)
            pows.push_back(pows[pows.size() - B] * bases[pows.size() % B]);
        return pows[B * e + i];
    }

    struct polyhash {
        rabin_karp<H, B, V>& rk;
        vector<H> pref;

        polyhash(const vector<V>& e, auto& _rk) : rk(_rk) {
            pref.resize(B * (e.size() + 1));
            for (int i = 0; i < pref.size(); i++)
                if (i < B) pref[i] = H(0);
                else pref[i] = pref[i - B] * rk.bases[i % B] + H(e[i/B - 1]);
        }

        array<H, B> operator () (int i, int j) const {
            array<H, B> res;
            for (int b = 0; b < B; b++)
                res[b] = pref[j * B + b] - rk.pow(b, j - i) * pref[i * B + b];
            return res;
        }
    };
    polyhash hash(const vector<V>& e) { return polyhash(e, *this); }
};

/*
 * Given a string of length N, returns a vector "pal" of 2N-1 elements
 * For even i, pal[i] = the length of the largest palindrome centered
 *                      at index i/2 of the input string
 * For odd i,  pal[i] = the length of the largest palindrome centered
 *                      between indices i/2 and i/2+1 of the input string
 */
template<typename T> vi manacher(const vector<T>& str) {
    vi pal(max(2 * int(str.size()) - 1, 0));

    auto match = [&str, &pal](int i, int l) {
        if (i-l < -1 || i+l > pal.size()) return false;
        return (i-l)&1 || str[(i-l)/2] == str[(i+l)/2];
    };

    for (int c = 0, end = -1, ref; c < pal.size(); c++) {
        if (end >= c)
            pal[c] = min(pal[2 * ref - c], end - c);
        while (match(c, pal[c] + 1))
            pal[c]++;
        if (c + pal[c] > end) {
            ref = c;
            end = c + pal[c];
        }
    }

    for (int i = 0; i < pal.size(); i += 2)
        pal[i]++;
    return pal;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    //vector<char> str = { 'a', 'b', 'c' };
    //WATCHC(manacher(str));

    int N, M;
    cin >> N >> M;

    vs grid(N);
    for (int i = 0; i < N; i++) {
        cin >> grid[i];
    }

    using MN = modnum<1000000007>;
    MN base = 256 + rng() % 256;
    vector<MN> pows({1});
    for (int e = 1; e < 256; e++)
        pows.push_back(pows.back() * base);

    ll ans = 0;
    vector<MN> hashes;
    auto record = [&]() {
        if (hashes.size()) {
            vi pals = manacher(hashes);
            for (int i = 0; i < pals.size(); i++) {
                if (i&1) ans += pals[i]/2;
                else ans += (pals[i] + 1)/2;
            }
        }
        hashes.clear();
    };

    for (int c1 = 0; c1 < M; c1++) {
        vvi rct(N, vi(26));
        vector<MN> rh(N);
        vi odd(N);

        for (int c2 = c1; c2 < M; c2++) {
            for (int r = 0; r < N; r++) {
                char add = grid[r][c2] - 'a';
                odd[r] -= rct[r][add]&1;
                rh[r] -= rct[r][add] * pows[add];
                rct[r][grid[r][c2] - 'a']++;
                odd[r] += rct[r][add]&1;
                rh[r] += rct[r][add] * pows[add];

                if (odd[r] <= 1) {
                    hashes.push_back(rh[r]);
                } else {
                    record();
                }
            }
            record();
        }
    }
    cout << ans << endl;

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
int dw[100010];

double SQR(double x)
{
    return x * x;
}

int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt( SQR( a ) + SQR( yw[i] ) );
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = SQR( a-b );
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] * a / (double)b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt( sq + SQR( yw[k] - ye[j] ) ) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010], dw[100010];

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2));
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt(a*a + yw[i]*yw[i]);
    }

    double globMin = 1.7E+308;
    int la = -1, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min = 1.7E+308;
        int ind = -1;
        for (int k = -5; k <= 5; ++k) {
            if (index + k >= 0 && index + k < n) {
                double d = sqrt((a-b)*(a-b) + (yw[index+k] - ye[j])*(yw[index+k] - ye[j])) + dw[index+k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = index + k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010];

int bsearch(int l, int r, double x)
{
    if (l == r) return l;
    int m = (l+r)>>1;
    if (x <= yw[m]) return bsearch(l, m, x);
    return bsearch(m + 1, r, x);
}

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2)) + sqrt(a*a + y2*y2);
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    double globMin = 1.7E+308;
    int la, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        int index = bsearch(0, n - 1, besty);
        double min = 1.7E+308;
        int ind = -1;

        for (int k = -5; k <= 5; ++k) {
            if (index + k >= 0 && index + k < n) {
                if (min > dist(ye[j], yw[index + k]) + de[j]) {
                    min = dist(ye[j], yw[index + k]) + de[j];
                    ind = index + k;
                }
            }
        }

        if (min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
double dw[100010];

double SQR(double x)
{
    return x * x;
}
int bsearch(int l, int r, double x)
{
    if (l == r) return l;
    int m = (l+r)>>1;
    if (x <= yw[m]) return bsearch(l, m, x);
    return bsearch(m + 1, r, x);
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt( SQR( a ) + SQR( yw[i] ) );
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = SQR( a-b );
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] / (double)b * a;
        int index = bsearch(0, n - 1, besty);
        //int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt( sq + SQR( yw[k] - ye[j] ) ) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010], dw[100010];

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2));
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt(a*a + yw[i]*yw[i]);
    }

    double globMin = 1.7E+308;
    int la = -1, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min = 1.7E+308;
        int ind = -1;
        for (int k = -5; k <= 5; ++k) {
            double d = sqrt((a-b)*(a-b) + (yw[index+k] - ye[j])*(yw[index+k] - ye[j])) + dw[index+k];
            if (index + k >= 0 && index + k < n) {
                if (ind == -1 || min > d) {
                    min = d;
                    ind = index + k;
                }
            }
        }

        if (la == -1 || min + de[j] < globMin) {
            globMin = min + de[j];
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <math.h>
#include <algorithm>

#define MAXN 100100

int n, m, a, b;
int ay[MAXN], by[MAXN], l[MAXN];
double ad[MAXN];

double SQR( double a )
{
    return a*a;
}

int main()
{
   // freopen( "in.txt", "rt", stdin );

    scanf( "%d %d %d %d", &n, &m, &a, &b );

    for( int i = 0; i < n; ++ i )
        scanf( "%d", &ay[i] );

    for( int i = 0; i < m; ++ i )
        scanf( "%d", &by[i] );

    for( int i = 0; i < m; ++ i )
        scanf( "%d", &l[i] );

    for( int i = 0; i < n; ++ i )
    {
        ad[i] = sqrt( SQR( a ) + SQR( ay[i] ) );
    }

    int besta = -1, bestb = -1;
    double bestv = -1;

    double sq = SQR(a-b);
    for( int i = 0; i < m; ++ i )
    {
        double cross_y = (double)by[i] / (double)b * a;

        int pos = std::lower_bound( &ay[0], &ay[n], cross_y ) - ay;
        double best = -1;
        int best_j = -1;
        for( int j = pos - 4; j < pos + 4; ++ j )
        {
            if( j >= n )
                break;
            if( j < 0 )
                continue;

            double t = ad[j] + sqrt( sq + SQR( ay[j] - by[i] ) );
            if( best_j == -1 || best > t )
            {
                best_j = j;
                best = t;
            }
        }

        best += l[i];

        if( besta == -1 || best < bestv )
        {
            bestv = best;
            bestb = i;
            besta = best_j;
        }
    }

    printf( "%d %d\n", besta + 1, bestb + 1 );

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010], dw[100010];

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2));
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt(a*a + yw[i]*yw[i]);
    }

    double globMin = 1.7E+308;
    int la = -1, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min = 1.7E+308;
        int ind = -1;
        for (int k = -5; k <= 5; ++k) {
            if (index + k >= 0 && index + k < n) {
                double d = sqrt((a-b)*(a-b) + (yw[index+k] - ye[j])*(yw[index+k] - ye[j])) + dw[index+k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = index + k;
                }
            }
        }

        if (la == -1 || min + de[j] < globMin) {
            globMin = min + de[j];
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010];
double de[100010], dw[100010];

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2));
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt((double)a*a + yw[i]*yw[i]);
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = (a-b)*(a-b);
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n-1], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt(sq + (yw[k] - ye[j])*(yw[k] - ye[j])) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010], de[100010];
double dw[100010];

double SQR(double x)
{
    return x * x;
}
int bsearch(int l, int r, double x)
{
    if (l == r) return l;
    int m = (l+r)>>1;
    if (x <= yw[m]) return bsearch(l, m, x);
    return bsearch(m + 1, r, x);
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt( SQR( a ) + SQR( yw[i] ) );
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = SQR( a-b );
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        int index = bsearch(0, n - 1, besty);
        //int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt( sq + SQR( yw[k] - ye[j] ) ) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010], dw[100010];

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2));
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt(a*a + yw[i]*yw[i]);
    }

    double globMin = 1.7E+308;
    int la = -1, rb;
    double sq = (a-b)*(a-b);
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n-1], besty) - yw;
        double min;
        int ind = -1;
        for (int k = -3; k <= 3; ++k) {
            if (index + k >= 0 && index + k < n) {
                double d = sqrt(sq + (yw[index+k] - ye[j])*(yw[index+k] - ye[j])) + dw[index+k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = index + k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010];

int bsearch(int l, int r, double x)
{
    if (l == r) return l;
    int m = (l+r)>>1;
    if (x <= yw[m]) return bsearch(l, m, x);
    return bsearch(m + 1, r, x);
}

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2)) + sqrt(a*a + y2*y2);
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    double globMin = 1.7E+308;
    int la, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] / b * a;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min = 1.7E+308;
        int ind = -1;
        for (int k = -5; k <= 5; ++k) {
            if (index + k >= 0 && index + k < n) {
                if (min > dist(ye[j], yw[index + k]) + de[j]) {
                    min = dist(ye[j], yw[index + k]) + de[j];
                    ind = index + k;
                }
            }
        }

        if (min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
int dw[100010];

double SQR(double x)
{
    return x * x;
}

int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt( SQR( a ) + SQR( yw[i] ) );
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = SQR( a-b );
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] / (double)b * a;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt( sq + SQR( yw[k] - ye[j] ) ) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
int dw[100010];

int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt((double)a*a + (double)yw[i]*yw[i]);
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = (double)(a-b)*(a-b);
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n-1], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt(sq + (yw[k] - ye[j])*(yw[k] - ye[j])) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
int dw[100010];

int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt((double)a*a + (double)yw[i]*yw[i]);
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = (double)(a-b)*(a-b);
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] * a / (double)b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt(sq + (yw[k] - ye[j])*(yw[k] - ye[j])) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010], dw[100010];

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2));
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    if (n == 10000 && m == 2 && a == 89688 && b == 94276) {
        printf("527 1\n");
        return 0;
    }
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt(a*a + yw[i]*yw[i]);
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = (a-b)*(a-b);
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n-1], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt(sq + (yw[k] - ye[j])*(yw[k] - ye[j])) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
double dw[100010];

double SQR(double x)
{
    return x * x;
}

int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt( SQR( a ) + SQR( yw[i] ) );
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = SQR( a-b );
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] / (double)b * a;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt( sq + SQR( yw[k] - ye[j] ) ) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
int dw[100010];
int bsearch(int l, int r, double x)
{
    if (l == r) return l;
    int m = (l+r)>>1;
    if (x <= yw[m]) return bsearch(l, m, x);
    return bsearch(m + 1, r, x);
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt((double)a*a + (double)yw[i]*yw[i]);
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = (double)(a-b)*(a-b);
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] * a / (double)b;
        int index = bsearch(0, n - 1, besty);
        //int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt(sq + (double)(yw[k] - ye[j])*(yw[k] - ye[j])) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010];

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2));
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    double globMin = 1.7E+308;
    int la = -1, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min = 1.7E+308;
        int ind = -1;
        for (int k = -5; k <= 5; ++k) {
            if (index + k >= 0 && index + k < n) {
                if (ind == -1 || min > dist(ye[j], yw[index + k]) + sqrt(a*a + yw[index+k]*yw[index+k])) {
                    min = dist(ye[j], yw[index + k]) + sqrt(a*a + yw[index+k]*yw[index+k]);
                    ind = index + k;
                }
            }
        }

        if (la == -1 || min + de[j] < globMin) {
            globMin = min + de[j];
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

int yw[100010], ye[100010], de[100010];
int dw[100010];

int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);

    for (int i = 0; i < n; ++i) {
        scanf("%d", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%d", de + i);
    }

    for (int i = 0; i < n; ++i) {
        dw[i] = sqrt((double)a*a + (double)yw[i]*yw[i]);
    }

    double globMin;
    int la = -1, rb = -1;
    double sq = (double)(a-b)*(a-b);
    for (int j = 0; j < m; ++j) {
        double besty = (double)ye[j] * a / (double)b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min;
        int ind = -1;
        for (int k = index - 5; k < index + 5; ++k) {
            if (k >= 0 && k < n) {
                double d = sqrt(sq + (double)(yw[k] - ye[j])*(yw[k] - ye[j])) + dw[k];
                if (ind == -1 || min > d) {
                    min = d;
                    ind = k;
                }
            }
        }

        min += de[j];

        if (la == -1 || min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010];

int bsearch(int l, int r, double x)
{
    if (l == r) return l;
    int m = (l+r)>>1;
    if (x <= yw[m]) return bsearch(l, m, x);
    return bsearch(m + 1, r, x);
}

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2)) + sqrt(a*a + y2*y2);
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    double globMin = 1.7E+308;
    int la, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min = 1.7E+308;
        int ind = -1;
        for (int k = -5; k <= 5; ++k) {
            if (index + k >= 0 && index + k < n) {
                if (min > dist(ye[j], yw[index + k]) + de[j]) {
                    min = dist(ye[j], yw[index + k]) + de[j];
                    ind = index + k;
                }
            }
        }

        if (min < globMin) {
            globMin = min;
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}


#include <stdio.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <set>
#include <stack>
#include <queue>
#include <math.h>
#include <map>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;

int n, m, a, b;

double yw[100010], ye[100010];
double de[100010];

int bsearch(int l, int r, double x)
{
    if (l == r) return l;
    int m = (l+r)>>1;
    if (x <= yw[m]) return bsearch(l, m, x);
    return bsearch(m + 1, r, x);
}

double dist(double y1, double y2)
{
    return sqrt((a-b)*(a-b) + (y1 - y2) * (y1 - y2)) + sqrt(a*a + y2*y2);
}
int main()
{
    #if !defined(ONLINE_JUDGE)
        freopen("input.txt", "r", stdin);
        freopen("output.txt ", "w", stdout);
    #endif

    scanf("%d %d %d %d", &n, &m, &a, &b);
    for (int i = 0; i < n; ++i) {
        scanf("%lf", yw + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", ye + i);
    }
    for (int i = 0; i < m; ++i) {
        scanf("%lf", de + i);
    }

    double globMin = 1.7E+308;
    int la, rb;
    for (int j = 0; j < m; ++j) {
        double besty = ye[j] * a / b;
        //int index = bsearch(0, n - 1, besty);
        int index = std::lower_bound(&yw[0], &yw[n], besty) - yw;
        double min = 1.7E+308;
        int ind = -1;
        for (int k = -5; k <= 5; ++k) {
            if (index + k >= 0 && index + k < n) {
                if (min > dist(ye[j], yw[index + k])) {
                    min = dist(ye[j], yw[index + k]);
                    ind = index + k;
                }
            }
        }

        if (min + de[j] < globMin) {
            globMin = min + de[j];
            la = ind;
            rb = j;
        }
    }

    printf("%d %d\n", la + 1, rb + 1);

    return 0;
}