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

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


//COPY THE BLACKBOX, there is no need to change anything in it.
//Check the main function at bottom for USAGE

//****************BLACKBOX START*****************
//START COPYING FROM HERE

typedef int ll;

class Hash {
  public:
  	static int hash(int x){
  		return hash({0,0,x});
  	}
  	static int hash(tuple<int,int>x){
  		return hash({0,get<0>(x),get<1>(x)});
  	}
  	static int hash(tuple<int,int,int>x){
  		int k=get<0>(x),u=get<1>(x),v=get<2>(x);
  		return k*1873*1873 + u*1873 + v;
  	}
};

class Graph {

	bool is_directed;

	public:
		vector<vector<pair<int,ll>>>adj;
    int n,N=2000000;

		Graph(int n_, bool is_directed_){
			n=n_; is_directed = is_directed_;
			adj.resize(N,vector<pair<int,ll>>());
		}

		int hash(int u, int v){
			return Hash::hash({u,v});
		}
		int hash(int u, int v, int k){
			return Hash::hash({u,v,k});
		}

		void add_edge(int uR, int vR, ll c=0){
		  int u=Hash::hash(uR), v=Hash::hash(vR);
		  add_edge_internal(u,v,c);
		}
		void add_edge(tuple<int,int> uR, tuple<int,int> vR, ll c=0){
		  int u=Hash::hash(uR), v=Hash::hash(vR);
		  add_edge_internal(u,v,c);
		}
			void add_edge(tuple<int,int,int> uR, tuple<int,int,int> vR, ll c=0){
		  int u=Hash::hash(uR), v=Hash::hash(vR);
		  add_edge_internal(u,v,c);
		}


	private :

	  void add_edge_internal(int u, int v, ll c=0){
			add_edge_weighted_undirected(u,v,c);
			if(!is_directed)
				add_edge_weighted_undirected(v,u,c);
		}
		void add_edge_weighted_undirected(int u, int v, ll c) {
			pair<int,ll>p = make_pair(v,c);
			adj[u].push_back(p);
		}

};

class BFS {
    vector<ll>min_dist_from_source;
    vector<bool> visited;
    Graph *g;

    public:
      BFS(Graph *g_) {
          g = g_;
          clear();
      }

	  	void clear() {
  			min_dist_from_source.clear();
  			min_dist_from_source.resize(g->N,-1);
  			visited.clear();
  			visited.resize(g->N, false);
  		}


      void run(int sourceR) {
        int source = Hash::hash(sourceR);
        run_internal(source);
      }
      void run(tuple<int,int> sourceR) {
        int source = Hash::hash(sourceR);
        run_internal(source);
      }
      void run(tuple<int,int,int> sourceR) {
        int source = Hash::hash(sourceR);
        run_internal(source);
      }


      int min_dist(int targetR){
      	int target = Hash::hash(targetR);
      	return min_dist_internal(target);
      }
      int min_dist(tuple<int,int> targetR){
      	int target = Hash::hash(targetR);
      	return min_dist_internal(target);
      }
      int min_dist(tuple<int,int,int> targetR){
      	int target = Hash::hash(targetR);
      	return min_dist_internal(target);
      }

      bool is_visited(int targetR){
      	int target = Hash::hash(targetR);
      	return is_visited_internal(target);
      }
      bool is_visited(tuple<int,int> targetR){
      	int target = Hash::hash(targetR);
      	return is_visited_internal(target);
      }
      bool is_visited(tuple<int,int,int> targetR){
      	int target = Hash::hash(targetR);
      	return is_visited_internal(target);
      }

  private:
    void run_internal(int source) {
			queue<int> q;
			q.push(source);

			visited[source] = true;
			min_dist_from_source[source] = 0;

			while(!q.empty()) {
				int cur_node = q.front();
				for (unsigned int i = 0; i < (g->adj[cur_node]).size(); ++i) {
					int adj_node =  (g->adj[cur_node])[i].first;
					if (visited[adj_node] == false) {
						visited[adj_node] = true;
						min_dist_from_source[adj_node] = min_dist_from_source[cur_node] + 1;
						q.push(adj_node);
					}
				}
				q.pop();
			}

			return;
    }

    int min_dist_internal(int target){
    	return min_dist_from_source[target];
    }

    bool is_visited_internal(int target){
    	return visited[target];
    }

};
//END COPYING HERE
//********************BLACKBOX END******************

int main() {

    int n,m; cin>>n>>m;
    
    //initialise graph with `n*m` nodes, each cell is a node
    Graph g(n*m,true);
    
	//intiatialise a 2D array of size `n*m
    vector<vector<char>>grid(n,vector<char>(m));
   
    for(int i=0;i<n;i++)
      for(int j=0;j<m;j++){
        cin>>grid[i][j];
       
      }
   
   
   //************GRAPH CONTRUCTION BEGIN*****************
   
   //iterate through every cell
    for(int i=0;i<n;i++)
      for(int j=0;j<m;j++) {
      	
      	//if current cell is floor
        if(grid[i][j]=='.') {

		// if cell above is withing the grid and a floor, create an Edge from `current cell -> cell above` 
          if(i-1>=0 && grid[i-1][j]=='.') 
            g.add_edge({i,j},{i-1,j});
        	
        // if cell below is withing the grid and a floor, create an Edge from `current cell -> cell below` 	
          if(i+1<n && grid[i+1][j]=='.') 
            g.add_edge({i,j},{i+1,j});

		// if cell left is withing the grid and a floor, create an Edge from `current cell -> cell left` 
		  if(j-1>=0 && grid[i][j-1]=='.')
            g.add_edge({i,j},{i,j-1});
         
         // if cell right is withing the grid and a floor, create an Edge from `current cell -> cell right` 
          if(j+1<m && grid[i][j+1]=='.')
            g.add_edge({i,j},{i,j+1});
        }
      }
      //************GRAPH CONTRUCTION END*****************
      
   
   //Since the number of nodes is high, and the Blackbox BFS returns the vector min_dist of size 'n*m', returning an array of size x takes time x. We might get TLE as the BFS is being called multiple times, requiring returning min_dist multiple times.
   //Therefore we need to go into the Blackbox and return the pointer to the min_dist instead of the whole array. Returning a point take 1 unit time only.
   
  
   int num_rooms=0;
   
   BFS bfs(&g);
   //iterate through every cell
    for(int i=0;i<n;i++)
      for(int j=0;j<m;j++){
    	//if the current cell is not reachable from any of the cell we already iterated, then we have found a new room	
      if(!bfs.is_visited({i,j}) && grid[i][j]=='.'){
        num_rooms++;
        
       //BFS from `i,j` will visit every cell reachable from `i,j`, aka in the same connected component(room) as `i,j`
        //`min_dist[k]` for all the nodes reachable from `i,j` will be the shortest path length from node `i,j` to node `k`
        bfs.run({i,j});
      }
    }
    
    
    cout<<num_rooms<<'\n';
    
    return 0;


  }