CSES - Shared codeLink to this code: https://cses.fi/paste/f3ea0f469366ebbd2b6c58/
#include <iostream>
#include <vector>
#include <unordered_map>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <unordered_set>
#include <map>
#include <queue>
#include <string>
#include <sstream>
using namespace std;

#define int long long
#define INF 1e18
#define pb push_back
#define FAST ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define ppb pop_back
#define lld long double
#define mod 1000000007
#define bn << '\n';
#define cbbn cout << '\n\n';
#define cbn cout << '\n';
#define debug cout << "HERE----------------------------\n";
#define all(a) a.begin(), a.end()
#define rall(a) a.rbegin(), a.rend()
#define vi vector<int>
#define vs vector<string>
#define vb vector<bool>
#define rep(i, a, n) for (int i = a; i < n; i++)
#define repr(i, n, a) for (int i = n; i >= a; i--)
#define pii pair<int, int>
#define vpi vector<pair<int, int>>
#define vvi vector<vector<int>>
#define vvpi vector<vector<pair<int, int>>>
#define sz(a) a.size()
#define clean(v,x) memset(v,x,sizeof(v));

template <typename T, typename T1> T amax(T &a, T1 b){if (b > a)a = b;	return a;}
template <typename T, typename T1> T amin(T &a, T1 b){if (b < a)a = b;return a;}
template <typename T> std::istream &operator>>(std::istream &input, std::vector<T> &data){for (T &x : data)input >> x;return input;}
template <typename T> std::ostream &operator<<(std::ostream &output, const std::pair<T, T> &data){output << "(" << data.first << "," << data.second << ")";	return output;}
template <typename T> std::ostream &operator<<(std::ostream &output, const std::vector<T> &data){for (const T &x : data)	output << x << " ";	return output;}

// Custom Hash For Unordered set  --> https://stackoverflow.com/questions/29855908/c-unordered-set-of-vectors
// Use this by -> unordered_set<vector<int>, VectorHash>mp;

//=============================================================================================================================


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// DO NOT TOUCH BEFORE THIS LINE ///////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//=================================================SOLVE-ANS===================================================================

struct DSU {
    //nodes are 1 base
    int SetCnt, FirstSetCnt;
    vector<int> par, sz;

    //constructor
    DSU (const int &x) {
        SetCnt = x;
        FirstSetCnt = x;
        par.resize(x + 1);
        sz.resize(x + 1);
        fill(par.begin(), par.end(), -1);
        fill(sz.begin(), sz.end(), 1);
    }
    //finding root of a vertex
    int find(int x) {
        return (par[x] == -1? x: par[x] = find(par[x]));
    }

    //merging two component
    bool merge(int x, int y) {
        int p1 = find(x), p2 = find(y);
        SetCnt -= (p1 != p2);
        if (sz[p1] > sz[p2])
            swap(p1, p2);
        if (p1 != p2)
            sz[p2] += sz[p1];
        return (p1 == p2? false: (par[p1] = p2) | 1);
    }

    //meging two DSU (adding every edge in second DSU to first DSU) ---> O(n.log*(n))
    bool joinDSU(DSU &x) {
        if (x.size() != FirstSetCnt)
            return false;
        for (int i = 1; i <= FirstSetCnt; i++) {
            int p1 = find(i), p2 = x.find(i);
            if (p1 != p2)
                merge(p1, p2);
        }
        return true;
    }

    //clear DSU
    void clear() {
        fill(par.begin(), par.end(), -1);
        fill(sz.begin(), sz.end(), 1);
        SetCnt = FirstSetCnt;
    }

    //number of nodes in component of node x
    int size_cmp(const int &x) {
        return sz[find(x)];
    }

    //number of components
    int num_cmp() {
        return SetCnt;
    }

    //number of nodes
    int size() {
        return FirstSetCnt;
    }
};



void Here_We_Go()
{
    int n,m;
    cin>>n>>m;
    vs v(n);
    cin>>v;
    DSU a(n*m + 10);
    int ans=0;
    rep(i,0,n)
    {
        rep(j,0,m)
        {
            if(v[i][j]=='.')
            {
                int p1=a.find(i*m + j);
                ans++;
                if(i-1>=0 && v[i-1][j]=='.')
                {
                    int p2=a.find((i-1)*m + j);
                    if(p1!=p2)
                    {
                        ans--;
                        a.merge(i*m +j,(i-1)*m + j );
                    }
                }
                if(i+1<n && v[i+1][j]=='.')
                {
                    int p2=a.find((i+1)*m + j);
                    if(p1!=p2)
                    {
                        ans--;
                        a.merge(i*m +j,(i+1)*m + j );
                    }
                }
                if(j-1>=0 && v[i][j-1]=='.')
                {
                    int p2=a.find((i)*m + j-1);
                    if(p1!=p2)
                    {
                        ans--;
                        a.merge(i*m +j,(i)*m + j-1 );
                    }
                }
                if(j+1<m && v[i][j+1]=='.')
                {
                    int p2=a.find((i)*m + j+1);
                    if(p1!=p2)
                    {
                        ans--;
                        a.merge(i*m +j,(i)*m + j+1 );
                    }
                }
            }
        }
    }
    cout<<ans bn
}

int32_t main()
{

	FAST
	// #ifndef ONLINE_JUDGE
	// 	freopen("input.txt", "r", stdin);
	// 	freopen("output.txt", "w", stdout);
	// #endif
	int tc = 1;
	//cin >> tc;
	rep(i, 1, tc + 1)
	{
		//cout << "Case #" << i << ": ";
		Here_We_Go();
	}
}