#include<bits/stdc++.h>
using namespace std;
ifstream f("dfs.in");
ofstream g("dfs.out");
//pentru APM
struct compare{
bool operator()(const vector<int> &a, const vector<int> &b){
return a[2]>b[2]; //compar dupa cost
}
};
//pentru Dijsktra
struct compare2{
bool operator()(const pair<int,int> &a, const pair<int,int> &b){
return a.second>b.second; //compar dupa cost
}
};
class Graf {
private:
int m_nodes,m_edges,m_cost;
public:
void setMCost(int mCost);
private:
bool m_isDirected;
bool m_haveCost;
vector<int> adjList[100001];
vector<int> rev_adjList[101],component[100001];
vector<pair<int,int>> adjList_withCost[10001];
public:
Graf(int mNodes, int mEdges, bool mIsDirected, bool mHaveCost);
void add_Edge(int from, int to);
//problema BFS infoarena
// al i-lea numar reprezinta numarul minim de arce ce trebuie parcurse de la nodul S la nodul i.
void bfs(int node,vector<int> visited, vector<int>& cost,int &last);
//-----------------------------
//problema DFS-----------------
void dfs(int node,vector<int>& visited,int nrcomp);
int nr_comp_conexe(vector<int>& visited);
//-------------------------------
//problema CTC
void first_DFS_CTC(int node,vector<int>& visited,stack<int>& st);
void reverse_CTC();
void second_DFS_CTC(int node,vector<int>& visited,int nr);
void nr_SCCs(int &nr,vector<int>& visited);
const vector<int> *getComponent() const;
//Havel Hakimi
int verify(vector<int> grades);
string answer(vector<int> grades);
//APM
void apm(int node,vector<int>& visited,priority_queue<vector<int>,vector<vector<int>>,compare>& heap,vector<pair<int,int>>& muchii,int& cost_total);
void Dijkstra(int node,vector<int>& visited, vector<int>& dist,priority_queue<pair<int,int>,vector<pair<int,int>>,compare2>& heap);
void topo(unordered_map<int,int>& intern_grade,vector<int>& topo_sort,queue<int>& q);
//vector<vector<int>> RoyFloyd(int cost[101][101]);
void euler_cycle(int start,vector<int> &ans,unordered_map<int,int> to,unordered_map<int,int> from,unordered_map<int,int> d);
};
const vector<int> *Graf::getComponent() const {
return component;
}
//constructorul
Graf::Graf(int mNodes, int mEdges, bool mIsDirected, bool mHaveCost) : m_nodes(mNodes), m_edges(mEdges),
m_isDirected(mIsDirected),
m_haveCost(mHaveCost) {
}
void Graf::setMCost(int mCost) {
m_cost = mCost;
}
//adaugare muchii pentru orientat si neorientat
void Graf::add_Edge(int from, int to) {
if(m_haveCost==0) {
if (m_isDirected == 0) //este neorientat
{
adjList[from].push_back(to);
adjList[to].push_back(from);
} else //este orientat
adjList[from].push_back(to);
}
else{
if (m_isDirected == 0) //este neorientat
{
adjList_withCost[from].push_back(make_pair(to,m_cost));
adjList_withCost[to].push_back(make_pair(from,m_cost));
} else //este orientat
adjList_withCost[from].push_back(make_pair(to,m_cost));
}
}
void Graf::bfs(int node, vector<int> visited, vector<int>& cost, int &last) {
int a;
queue<int> q;
visited.assign(m_nodes+1,0);
cost.assign(m_nodes+1,-1);
visited[node]=1;
cost[node]=0;
q.push(node);
while(!q.empty()){
node=q.front();
last=node;
a=cost[node];
q.pop();
for(auto i:adjList[node])
{
if(visited[i]==0){
visited[i]=1;
cost[i]=a+1;
q.push(i);
}
}
}
}
void Graf::dfs(int node, vector<int> &visited,int nrcomp) {
int i;
visited[node]=nrcomp;
for(i=0;i<adjList[node].size();i++){
if(!visited[adjList[node][i]]){
dfs(adjList[node][i],visited,nrcomp);
}
}
}
int Graf::nr_comp_conexe(vector<int>& visited) {
int nrcomp=0;
for(int i=0;i<=m_nodes;i++)
{
visited.push_back(0);
}
for(int i=1;i<=m_nodes;i++)
{
if(visited[i]==0){
nrcomp++;
dfs(i,visited,nrcomp);
}
}
return nrcomp;
}
void Graf::first_DFS_CTC(int node, vector<int> &visited, stack<int> &st) {
visited[node]=1;
for(auto i:adjList[node])
if(visited[i]==0)
first_DFS_CTC(i,visited,st);
st.push(node);
}
void Graf::reverse_CTC() {
for(int i=0;i<m_nodes;i++){
for(auto j:adjList[i])
rev_adjList[j].push_back(i);
}
}
void Graf::second_DFS_CTC(int node, vector<int> &visited, int nr) {
component[nr].push_back(node+1);
visited[node]=1;
for(auto i:rev_adjList[node])
if(visited[i]==0)
{
second_DFS_CTC(i,visited,nr);
}
}
void Graf::nr_SCCs(int &nr,vector<int>& visited){
nr=0;
stack<int> st;
for(int i=0;i<m_nodes;i++){
for(auto j:adjList[i])
component[j].push_back(i);
}
for(int i=0;i<m_nodes;i++){
visited.push_back(0);
}
for(int i=0;i<m_nodes;i++){
if(visited[i]==0)
first_DFS_CTC(i,visited,st);
}
reverse_CTC();
for(int i=0;i<m_nodes;i++){
visited[i]=0;
}
while(!st.empty()){
int a=st.top();
st.pop();
if(visited[a]==0){
nr++;
second_DFS_CTC(a,visited,nr);
}
}
}
//Havel Hakimi
int Graf::verify(vector<int> grades){
int sum=0;
for(int i=0;i<grades.size();i++){
if(grades[i]>grades.size()-1)
return 0;
sum+=grades[i];
}
if(sum %2 == 1 )
return 0;
return 1;
}
string Graf::answer(vector<int> grades){
if(verify(grades)){
while(grades.size() != 0 ){
sort(grades.begin(),grades.end(), greater<int>());
int a=grades[0];
grades.erase(grades.begin());
for(int i=0;i<a;i++){
if(grades[i]-1<0)
return "NU";
else
grades[i]-=1;
}
}
return "DA";
}
return "NU";
}
//APM
void Graf::apm(int node,vector<int>& visited,priority_queue<vector<int>,vector<vector<int>>,compare>& heap,vector<pair<int,int>>& muchii,int& cost_total){
visited[node]=1;
for(int i=0;i<adjList_withCost[node].size();i++){
if(visited[adjList_withCost[node][i].first]==0) //parcurg lista de adiacenta si vad daca a fost vizitat fiecare nod
//bag in heap (u,v,cost(u,v))
{
vector<int> aux;
aux.push_back(node);
aux.push_back(adjList_withCost[node][i].first);
aux.push_back(adjList_withCost[node][i].second);
heap.push(aux);
}
}
while(heap.size()!=0){
vector<int> top;
top=heap.top();
heap.pop();
if(visited[top[0]]==1 && visited[top[1]]==0) //u e in apm, dar v nu
{
cost_total+=top[2];
muchii.push_back(make_pair(top[0],top[1])); //pun in muchii (u,v)
apm(top[1],visited,heap,muchii,cost_total);
}
}
}
//Dijkstra
void Graf::Dijkstra(int node,vector<int>& visited, vector<int>& dist,priority_queue<pair<int,int>,vector<pair<int,int>>,compare2>& heap){
visited[node]=1;
for(int i=0;i<adjList_withCost[node].size();i++){
if(visited[adjList_withCost[node][i].first]==0) //parcurg lista de adiacenta si vad daca a fost vizitat fiecare nod
//bag in heap (u,v,cost(u,v))
{
heap.push(make_pair(adjList_withCost[node][i].first,adjList_withCost[node][i].second+dist[node]));
}
}
while(heap.size()!=0){
pair<int,int> top;
top=heap.top();
heap.pop();
if(visited[top.first]== 0) //u e in apm, dar v nu
{
dist[top.first]=top.second;
Dijkstra(top.first,visited,dist,heap);
}
}
}
void Graf::topo(unordered_map<int,int>& intern_grade,vector<int>& topo_sort,queue<int>& q){
for(int i=1;i<=m_nodes;i++)
if(intern_grade[i]==0)
q.push(i);
while(q.size()!=0){
int node=q.front();
q.pop();
topo_sort.push_back(node);
for(int i=0;i<adjList[node].size();i++)
{
intern_grade[adjList[node][i]]=intern_grade[adjList[node][i]]-1;
if(intern_grade[adjList[node][i]]==0)
q.push(adjList[node][i]);
}
}
}
void Graf::euler_cycle(int start,vector<int> &ans,unordered_map<int,int> to,unordered_map<int,int> from,unordered_map<int,int> d){
stack<int> st;
unordered_map<int,int> visited;
int ok=0;
int urmatorul_nod;
for(int i=1;i<=m_nodes;i++)
{
if(d[i]%2 == 1)
{
ans.push_back(-1);
ok=1;
break;
}
}
if(ok==0) {
st.push(start);
while (!st.empty()) {
int node = st.top();
if (!adjList[node].empty()) {
int numar_muchie = adjList[node].back();
adjList[node].pop_back();
if (visited[numar_muchie] == 0) {
visited[numar_muchie] = 1;
if (to[numar_muchie] != node)
urmatorul_nod = to[numar_muchie];
else
urmatorul_nod = from[numar_muchie];
st.push(urmatorul_nod);
}
} else {
ans.push_back(st.top());
st.pop();
}
}
}
}
void solve_bfs(){
int n,m,S,x,y;
f>>n>>m>>S;
Graf graf(n,m,1,false);
for(int i=0;i<m;++i)
{
f>>x>>y;
graf.add_Edge(x,y);
}
vector<int> cost,visited;
int last;
graf.bfs(S,visited,cost,last);
for(int i=1;i<=n;i++)
g<<cost[i]<<" ";
}
void solve_dfs_nrCompConexe(){
int i,n,m,x,y,nrcomp=0;
f>>n>>m;
Graf graf(n,m,0,false);
vector<int> visited;
for(int i=0;i<m;++i)
{
f>>x>>y;
graf.add_Edge(x,y);
}
g<<graf.nr_comp_conexe(visited);
}
//-------------------------CTC--------------------
void solve_CTC()
{
int n,m,x,y;
f>>n>>m;
Graf graf(n,m,1,false);
for(int i=0;i<m;++i)
{
f>>x>>y;
graf.add_Edge(x-1,y-1);
}
vector<int> visited;
int nr=0;
graf.nr_SCCs(nr,visited);
g<<nr<<'\n';
for(int i=1;i<=nr;i++)
{
for(auto j:graf.getComponent()[i])
if(j!=0)
g<<j<<" ";
g<<'\n';
}
}
//-------------------------Havel Hakimi-------------------------
void solve_Havel_Hakimi(){
int n,x;
vector<int> grades;
f>>n;
Graf graf(n,0,0,0);
for(int i=0;i<n;i++)
{
f>>x;
grades.push_back(x);
}
g<<graf.answer(grades);
}
//------------------APM--------------
void solve_APM(){
int n,m,x,y,cost;
f>>n>>m;
Graf graf(n,m,0,1);
for(int i=0;i<m;++i)
{
f>>x>>y;
f>>cost;
graf.setMCost(cost);
graf.add_Edge(x,y);
}
//vector<int>& visited,priority_queue<vector<int>,vector<vector<int>>,compare>& heap,vector<pair<int,int>>& muchii,int& cost_total
int cost_total=0;
vector<pair<int,int>> muchii;
priority_queue<vector<int>,vector<vector<int>>,compare> heap;
vector<int> visited;
for(int i=0;i<=n;i++)
visited.push_back(0);
graf.apm(1,visited,heap,muchii,cost_total);
g<<cost_total<<'\n';
g<<muchii.size()<<'\n';
for(auto i:muchii)
//for(int j=0;j<i.size();j++)
g<<i.first<<" "<<i.second<<'\n';
}
//-------------------------Dijkstra-----------------
void solve_Dijkstra(){
int n,m,x,y,cost;
f>>n>>m;
Graf graf(n,m,1,1);
for(int i=0;i<m;++i)
{
f>>x>>y;
f>>cost;
graf.setMCost(cost);
graf.add_Edge(x,y);
}
vector<int> dist;
vector<int> visited;
priority_queue<pair<int,int>,vector<pair<int,int>>,compare2> heap;
for(int i=0;i<=n;i++)
{
visited.push_back(0);
dist.push_back(0);
}
graf.Dijkstra(1,visited,dist,heap);
for(int i=2;i<=n;i++){
g<<dist[i]<<" ";
}
}
//------------------Sortaret----------------------
void solve_Sortaret(){
int n,m,x,y;
vector<int> visited;
queue<int> q;
unordered_map<int,int> intern_grade;
vector<int> topo_sort;
f>>n>>m;
Graf graf(n,m,1,false);
for(int i=0;i<m;++i)
{
f>>x>>y;
intern_grade[y]+=1;
graf.add_Edge(x,y);
}
for(int i=0;i<=n;i++)
visited.push_back(0);
graf.topo(intern_grade,topo_sort,q);
for(int i=0;i<topo_sort.size();i++)
g<<topo_sort[i]<<" ";
}
void solve_DARB(){
int n,first_node,final,x,y;
f>>n;
Graf graf(n,n,0,false);
for(int i=0;i<n-1;++i)
{
f>>x>>y;
graf.add_Edge(x,y);
}
vector<int> cost,visited;
int last;
graf.bfs(1,visited,cost,first_node);
graf.bfs(first_node,visited,cost,final);
g<<cost[final]+1;
}
void solve_RoyFloyd(){
int n,x;
int cost[101][101],d[101][101];
f>>n;
for(int i=1;i<=n;i++)
for (int j=1; j<=n;j++) {
f >> cost[i][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
if (i!=j && cost[i][j] == 0) {
d[i][j] = 1001;
}
else {
d[i][j] = cost[i][j];
}
}
for (int k=1;k<=n;k++)
for (int i=1; i<=n;i++)
for (int j=1; j<=n; j++)
if (d[i][j]>d[i][k]+d[k][j]) {
d[i][j]=d[i][k]+d[k][j];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
g<<d[i][j]<<" ";
g<<endl;
}
}
void solve_eulerCycle(){
int i,x,y,n,m;
unordered_map<int,int> from,to,d;
f>>n>>m;
Graf graf(n,m,1,false);
for(int i=0;i<m;++i)
{
f>>x>>y;
d[x]++;
d[y]++;
from[i]=x;
to[i]=y;
graf.add_Edge(x,i);
graf.add_Edge(y,i);
}
vector<int> ans;
graf.euler_cycle(1,ans,to,from,d);
if(ans.size()==1)
g<<ans[0];
else
for(i=0;i<ans.size()-1;i++)
g<<ans[i]<<" ";
}
int main() {
//solve_bfs();
solve_dfs_nrCompConexe();
//solve_CTC();
//solve_Havel_Hakimi();
//solve_APM();
//solve_Dijkstra();
//solve_Sortaret();
//solve_DARB();
//solve_RoyFloyd();
//solve_eulerCycle();
return 0;
}