#include <bits/stdc++.h>
//#include "Graph.h"
//#include "Tree.h"
using namespace std;
ifstream f("dfs.in");
ofstream g("dfs.out");
vector<int> freq;
class Tree {
private:
int m_n,m_m;
public:
int findParent(int x,vector<int> &parent);
vector<string> DisjointMain(vector<vector<int>> questions);
Tree(int mN, int mM);
};
int Tree::findParent(int x,vector<int> &parent) {
while(parent[x]!=x)
x=parent[x];
return x;
}
vector<string> Tree::DisjointMain(vector<vector<int>> questions) {
vector<string> responses;
vector<int> parent;
vector<int> height;
int i;
for(i=0;i<m_n;++i){
parent.push_back(i);
height.push_back(1);
}
for(i=0;i<m_m;++i){
if(questions[i][0]==1){
int x=findParent(questions[i][1]-1,parent);
int y=findParent(questions[i][2]-1,parent);
if(height[x]>=height[y]){
parent[y]=x;
height[x]=height[x]+height[y];
}
else{
parent[x]=y;
height[y]=height[y]+height[x];
}
}
else if(questions[i][0]==2){
int x=findParent(questions[i][1]-1,parent);
int y=findParent(questions[i][2]-1,parent);
if(x==y)
responses.push_back("DA\n");
else
responses.push_back("NU\n");
}
}
return responses;
}
Tree::Tree(int mN, int mM) : m_n(mN), m_m(mM) {}
class Graph {
private:
int m_n,m_m;
bool m_oriented,m_cost;
vector<int> m_adjList[100001];
vector<pair<int,int>> m_adjListCost[100000];
//vector<int> m_visited;
public:
Graph(int mN, int mM, bool mOriented,bool mCosts);
void setNrNoduri(int mN);
void setNrMuchii(int mM);
void addEdge(int x,int y,int c=0);
void BFS(int x,int &distanta,vector<int> &dist,queue<int> &q,vector<int> &visited);
vector<int> distanceFromOneNodeToOthers(int s);
void DFSbiconex(int x,int parent,vector<int> &visited,vector<int> &low,vector<int> &id,int temp,stack<int> &st,vector<int> &aux,vector<vector<int>> &res);
vector<vector<int>> BiconexComponents();
void DFSctc(int x,vector<int> &visited,stack<int> &st);
void Kosaraju(int x,int node, vector<int> &visited,vector<vector<int>> &adjListTrans,vector<vector<int>> &ctc);
vector<int> CTC();
void DFS(int x/*,vector<int> &freq*/);
int numberOfComponents();
static bool comp(int a,int b){
return a>b;
}
bool HavelHakimi(vector<int> &grades);
bool verify(vector<int> &v);
bool HavelHakimiMain();
vector<int> SortareTopologica();
void DFSCriticalConnection(int x,int parrent,vector<int> &low,vector<int> &id,int timp,vector<vector<int>> &result,vector<int> &visited);
vector<vector<int>> CriticalConnection();
struct CustomCompare{
bool operator()(const vector<int> &a, const vector<int> &b){
return a[2]>b[2];
}
};
void Prim(int x,vector<int> &isInApm, priority_queue<vector<int>,vector<vector<int>>,CustomCompare> &heap,vector<pair<int, int>> &muchii, int cost);
vector<pair<int,int>> APM();
struct CustomCompare2{
bool operator()(const pair<int,int> &a, const pair<int,int> &b){
return a.second>b.second;
}
};
void Dijkstra(int x,vector<int> &isMarked, priority_queue<pair<int,int>,vector<pair<int,int>>,CustomCompare2> &heap, vector<int> &length);
vector<int> DijkstraResult();
void BFSdarb(int x,int varfUltim,queue<int> &q, vector<int> &visited);
int Darb();
vector<vector<int>> RoyFloyd(vector<vector<int>> matrix);
vector<int> Eulerian(int x,vector<int> &euler,vector<int> &visited,stack<int> &st);
vector<int> EulerianMain();
};
void Graph::addEdge(int x,int y,int c) {
if(m_cost==false){
if(m_oriented==false)
m_adjList[x].push_back(y);
else{
m_adjList[x].push_back(y);
m_adjList[y].push_back(x);
}
}
else{
if(m_oriented==false)
m_adjListCost[x].push_back(make_pair(y,c));
else{
m_adjListCost[x].push_back(make_pair(y,c));
m_adjListCost[y].push_back(make_pair(x,c));
}
}
}
void Graph::setNrNoduri(int mN) {
m_n = mN;
}
void Graph::setNrMuchii(int mM) {
m_m = mM;
}
void Graph::BFS(int x,int &distanta,vector<int> &dist,queue<int> &q,vector<int> &visited) {
int i;
q.push(x);
if(visited[x-1]==0){
dist[x-1]=distanta;
visited[x-1]++;
distanta++;
}
while(!q.empty()){
int first=q.front();
q.pop();
for(i=0;i<m_adjList[first].size();++i){
if(visited[m_adjList[first][i]-1]==0){
visited[m_adjList[first][i]-1]++;
dist[m_adjList[first][i]-1]=dist[first-1]+1;
q.push(m_adjList[first][i]);
}
}
}
}
vector<int> Graph::distanceFromOneNodeToOthers(int s) {
vector<int> dist;
queue<int> q;
vector<int> visited;
int distanta=0, i;
for(int i=0;i<m_n;++i)
visited.push_back(0);
BFS(s,distanta,dist,q,visited);
for(i=0;i<m_n;++i){
if(visited[i]==0){
dist[i]=-1;
}
}
return dist;
}
void Graph::DFSbiconex(int x, int parent,vector<int> &visited,vector<int> &low,vector<int> &id,int temp,stack<int> &st,vector<int> &aux,vector<vector<int>> &res) {
int i;
id[x]=temp;
low[x]=temp;
temp++;
st.push(x);
for(i=0;i<m_adjList[x].size();++i){
if(parent!=m_adjList[x][i]){
if(visited[m_adjList[x][i]]==0){
visited[m_adjList[x][i]]++;
DFSbiconex(m_adjList[x][i],x,visited,low,id,temp,st,aux,res);
low[x]=min(low[x],low[m_adjList[x][i]]);
if(id[x]<=low[m_adjList[x][i]]){
while(!st.empty() && st.top()!=m_adjList[x][i]){
aux.push_back(st.top()+1);
st.pop();
}
aux.push_back(m_adjList[x][i]+1);
st.pop();
aux.push_back(x+1);
res.push_back(aux);
aux.clear();
}
}
else{
low[x]=min(low[x],id[m_adjList[x][i]]);
}
}
}
}
vector<vector<int>> Graph::BiconexComponents() {
vector<int> visited,low,id;
int temp=0,cnt=0,i;
stack<int> st;
vector<vector<int>> res;
vector<int> aux;
for(i=0;i<m_n;++i){
visited.push_back(0);
id.push_back(0);
low.push_back(0);
}
for(i=0;i<m_n;++i){
if(visited[i]==0){
visited[i]++;
DFSbiconex(i,-1,visited,low,id,temp,st,aux,res);
while(!st.empty()){
st.pop();
}
}
}
return res;
}
void Graph::DFSctc(int x, vector<int> &visited,stack<int> &st) {
int i;
for(i=0;i<m_adjList[x].size();++i){
if(visited[m_adjList[x][i]-1]==0){
visited[m_adjList[x][i]-1]++;
//cout<<adjList[x][i]<<' ';
DFSctc(m_adjList[x][i],visited,st);
}
}
cout<<x<<' ';
st.push(x);
}
void Graph::Kosaraju(int x, int node, vector<int> &visited, vector<vector<int>> &adjListTrans, vector<vector<int>> &ctc) {
int i;
for(i=0;i<adjListTrans[x].size();++i){
if(visited[adjListTrans[x][i]-1]==1){
visited[adjListTrans[x][i]-1]++;
ctc[node].push_back(adjListTrans[x][i]);
Kosaraju(adjListTrans[x][i],node,visited,adjListTrans,ctc);
}
}
}
vector<int> Graph::CTC() {
vector<int> adjListTrans[100001], ctc[100001];
vector<int> visited;
stack<int> st;
vector<int> nodes;
int i;
for(i=0;i<m_n;++i)
visited.push_back(0);
for(i=0;i<visited.size();++i){
if(visited[i]==0){
visited[i]++;
DFSctc(i+1,visited,st);
}
}
int count=0;
while(!st.empty()){
int node=st.top();
if(visited[node-1]==1){
visited[node-1]++;
nodes.push_back(node);
ctc[node].push_back(node);
count++;
Kosaraju(node, node, visited, reinterpret_cast<vector<vector<int>> &>(adjListTrans),
reinterpret_cast<vector<vector<int>> &>(ctc));
}
st.pop();
}
return reinterpret_cast<const vector<int> &>(ctc);
}
void Graph::DFS(int x/*,vector<int> &freq*/) {
int i;
for(i=0;i<m_adjList[x].size();++i){
if(freq[m_adjList[x][i]-1]==0){ //verific daca am trecut prin nod
freq[m_adjList[x][i]-1]++;
//cout<<adjList[x][i]<<' ';
DFS(m_adjList[x][i]/*,freq*/);
}
}
}
int Graph::numberOfComponents() {
//vector<int> freq;
int count=0;
int i,j;
for(i=0;i<m_n;++i){
freq.push_back(0);
}
for(i=0;i<freq.size();++i){
if(freq[i]==0){ ///verific faca am trecut prin nod
//cout<<'\n';
freq[i]++;
count++; //numar componenta conexa
DFS(i+1/*,freq*/);
}
}
return count;
}
/*static bool Graph::comp(int a, int b) {
return a>b;
}*/
bool Graph::HavelHakimi(vector<int> &grades) {
int i;
sort(grades.begin(),grades.end(),comp);
while(grades.size()>0){
if(grades[0]>=grades.size())
return false;
for(i=1;i<=grades[0];++i){
grades[i]--;
if(grades[i]<0){
return false;
}
}
grades.erase(grades.begin());
sort(grades.begin(),grades.end(),comp);
}
return true;
}
bool Graph::verify(vector<int> &v) {
int sum=0,i;
for(i=0;i<v.size();++i){
sum+=v[i];
if(v[i]>=v.size())
return false;
}
if(sum%2==1)
return false;
return true;
}
bool Graph::HavelHakimiMain() {
vector<int> grades;
if(verify(grades)==true){
return HavelHakimi(grades);
}
else
return false;
}
vector<int> Graph::SortareTopologica() {
stack<int> st;
vector<int> visited;
vector<int> sortat;
int i;
for(i=0;i<m_n;++i){
visited.push_back(0);
}
for(i=0;i<visited.size();++i){
if(visited[i]==0){
visited[i]++;
DFSctc(i,visited,st);
}
}
while(!st.empty()){
sortat.push_back(st.top()+1);
st.pop();
}
return sortat;
}
void Graph::Prim(int x,vector<int> &isInApm, priority_queue<vector<int>,vector<vector<int>>,CustomCompare> &heap,vector<pair<int, int>> &muchii, int cost) {
isInApm[x]++;
int i;
for(i=0;i<m_adjListCost[x].size();++i){
if(isInApm[m_adjListCost[x][i].first]==0){
vector<int> aux;
aux.push_back(x);
aux.push_back(m_adjListCost[x][i].first);
aux.push_back(m_adjListCost[x][i].second);
heap.push(aux);
aux.clear();
}
}
vector<int> first;
while(heap.size()>0){
first=heap.top();
heap.pop();
if(isInApm[first[0]]==1 && isInApm[first[1]]==0){
muchii.push_back(make_pair(first[0],first[1]));
cost=cost+first[2];
Prim(first[1],isInApm,heap,muchii,cost);
}
else if(isInApm[first[0]]==0 && isInApm[first[1]]==1){
muchii.push_back(make_pair(first[0],first[1]));
cost=cost+first[2];
Prim(first[0],isInApm,heap,muchii,cost);
}
}
}
vector<pair<int, int>> Graph::APM() {
vector<int> isInApm;
priority_queue<vector<int>,vector<vector<int>>,CustomCompare>heap;
int cost=0;
vector<pair<int,int>> muchii;
int i;
for(i=0;i<m_n;++i)
isInApm.push_back(0);
Prim(0,isInApm,heap,muchii,cost);
return muchii;
}
void Graph::Dijkstra(int x,vector<int> &isMarked, priority_queue<pair<int,int>,vector<pair<int,int>>,CustomCompare2> &heap, vector<int> &length) {
int i;
isMarked[x]++;
for(i=0;i<m_adjListCost[x].size();++i){
if(isMarked[m_adjListCost[x][i].first]==0){
pair<int,int> aux;
heap.push(make_pair(m_adjListCost[x][i].first,m_adjListCost[x][i].second+length[x]));
}
}
pair<int,int> frt;
while(heap.size()>0){
frt=heap.top();
heap.pop();
if(isMarked[frt.first]==0){
length[frt.first]=frt.second;
Dijkstra(frt.first,isMarked,heap,length);
}
}
}
vector<int> Graph::DijkstraResult() {
vector<int> isMarked,length;
priority_queue<pair<int,int>,vector<pair<int,int>>,CustomCompare2>heap;
int i;
for(i=0;i<m_n;++i){
isMarked.push_back(0);
length.push_back(0);
}
Dijkstra(0,isMarked,heap,length);
return length;
}
void Graph::BFSdarb(int x,int varfUltim,queue<int> &q, vector<int> &visited) {
int i;
varfUltim=x;
q.push(x);
while(!q.empty()){
int first=q.front();
varfUltim=first;
q.pop();
for(i=0;i<m_adjList[first].size();++i){
if(visited[m_adjList[first][i]]==0){
visited[m_adjList[first][i]]++;
q.push(m_adjList[first][i]);
}
}
}
}
int Graph::Darb() {
vector<int> visited,dist;
queue<int> q;
int varfUltim,diametru=0;
int i;
int distanta=0;
for(i=0;i<m_n-1;++i) {
visited.push_back(0);
}
visited.push_back(0);
BFSdarb(0,varfUltim,q,visited);
diametru=0;
visited.clear();
visited.assign(m_n,0);
dist.assign(m_n,0);
int varf1=varfUltim;
BFS(varf1,distanta,dist,q,visited);
int varf2=varfUltim;
diametru=dist[varf2]+1;
return diametru;
}
vector<vector<int>> Graph::RoyFloyd(vector<vector<int>> matrix) {
//vector<vector<int>> matrix;
int i,j,k;
for(i=0;i<m_n;++i)
for(j=0;j<m_n;++j){
if(matrix[i][j]==0 && i!=j)
matrix[i][j]=2000;
}
for(k=0;k<m_n;++k){
for(i=0;i<m_n;++i){
for(j=0;j<m_n;++j){
if(matrix[i][j]>matrix[i][k]+matrix[k][j]){
matrix[i][j]=matrix[i][k]+matrix[k][j];
}
}
}
}
return matrix;
}
vector<int> Graph::Eulerian(int x,vector<int> &euler,vector<int> &visited,stack<int> &st) {
int i;
for(i=0;i<m_n;++i){
if(m_adjListCost[i].size()%2==1){
return {-1};
}
}
st.push(0);
while(!st.empty()){
int node=st.top();
if(m_adjListCost[node].size()!=0){
int next=m_adjListCost[node].back().first;
int j=m_adjListCost[node].back().second;
m_adjListCost[node].pop_back();
if(visited[j]==0){
visited[j]=1;
st.push(next);
}
}
else
{
euler.push_back(node);
st.pop();
}
}
return euler;
}
vector<int> Graph::EulerianMain() {
stack<int> st;
vector<int> euler;
vector<int> visited; //pentru muchii
int i;
visited.assign(m_m+1,0);
vector<int> eulerRez=Eulerian(0,euler,visited,st);
if(eulerRez[0]==-1)
return {-1};
return eulerRez;
}
Graph::Graph(int mN, int mM, bool mOrientat,bool mCosts) : m_n(mN), m_m(mM), m_oriented(mOrientat),m_cost(mCosts) {}
void Graph::DFSCriticalConnection(int x,int parrent,vector<int> &low,vector<int> &id,int timp,vector<vector<int>> &result,vector<int> &visited) {
id[x]=timp;
low[x]=timp;
timp++;
int i;
for(i=0;i<m_adjList[x].size();++i){
if(parrent!=m_adjList[x][i]){
if(visited[m_adjList[x][i]]==0){
visited[m_adjList[x][i]]++;
DFSCriticalConnection(m_adjList[x][i],x,low,id,timp,result,visited);
low[x]=min(low[x],low[m_adjList[x][i]]);
if(id[x]<low[m_adjList[x][i]])
result.push_back({x,m_adjList[x][i]});
}
else{
low[x]=min(low[x],id[m_adjList[x][i]]);
//cout<<x<<adjList[x][i]<<" ";
}
}
}
}
vector<vector<int>> Graph::CriticalConnection() {
int timp=0;
vector<vector<int>> result;
int parrent;
vector<int> visited,id,low;
int i;
for(i=0;i<m_n;++i){
visited.push_back(0);
low.push_back(-1);
id.push_back(-1);
}
visited[0]++;
DFSCriticalConnection(0,-1,low,id,timp,result,visited);
return result;
}
int main() {
int n,m;
int a,b,c;
bool oriented,cost;
f>>n>>m;
Graph gf(n,m,0,0);
int i;
for(i=0;i<m;++i){
f>>a>>b;
gf.addEdge(a,b);
}
g<<gf.numberOfComponents();
return 0;
}