stablegradients/pie-gem5-bysrc huggingface dataset:
// Original C++ solution (slower):
#include <bits/stdc++.h>



using namespace std;



const int N = 1e5 + 10;

const unsigned X = 1234567;

const unsigned R = 7531;



int n;

unsigned h[N];

vector <int> g[N];

map <unsigned, int> f;



void dfs (int u, int p) {

	unsigned now = 0;

	for (int v : g[u]) if (v != p) {

		dfs(v, u); 

		now += h[v];

	}

	h[u] = now * X + R;

	++f[h[u]];  

}



int main (int argc, char const *argv[]) {

	scanf("%d", &n);

	for (int i = 1; i < n; ++i) {

		int u, v;

		scanf("%d %d", &u, &v);

		g[u].push_back(v);

		g[v].push_back(u);

	}



	dfs(1, 0);



	long long ans = 0;

	for (auto it : f) {

		long long cnt = (long long) it.second;

		ans += (cnt * (cnt - 1LL))/2LL;

	}



	printf("%lld\n", ans);

	return 0;

}

// Optimized solution to the same problem (faster):
#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<vector>

#include<queue>

#include<stack>

#include<cmath>

#include<set>

#include<map>

using namespace std;

#define rep(i,a,n) for (int i=a;i<n;i++)

#define per(i,a,n) for (int i=n-1;i>=a;i--)

#define pb push_back

#define fi first

#define se second

#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;

typedef vector<int> VI;

typedef long long ll;

typedef pair<double,int> PII;

const ll p=998244353;

const ll mod=1e9+7;

const int maxn=1e5+100;

map<ll,int> mp;



struct node

{

    int to,next;

}e[maxn*2];



int tol=0;

int head[maxn];

void add(int u,int v)

{

    e[++tol].to=v,e[tol].next=head[u],head[u]=tol;

}

ll dfs(int u,int f)

{

    ll ans=1;

    for(int i=head[u];i;i=e[i].next)

    {

        int v=e[i].to;

        if(v==f) continue;

        ans=(ans+dfs(v,u)*mod%p)%p;

    }

    mp[ans]++;

    return ans;

}





int main()

{

    int n;

    scanf("%d",&n);

    rep(i,1,n)

    {

        int u,v;

        scanf("%d%d",&u,&v);

        add(u,v),add(v,u);

    }

    mp.clear();

    dfs(1,0);

    ll ans=0;

    map<ll,int>::iterator it;

    for(it=mp.begin();it!=mp.end();it++) ans+=1ll*(it->se)*(it->se-1)/2;

    printf("%lld\n",ans);

    return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

typedef pair<int,int>pii;

const int N = 1e5+5;

const ll p = 9901;

const ll mod = 1e9+7;

const double eps = 1e-8;

int n,m,k;

ll has[N];

vector<int>edg[N];

map<ll,ll>mp;

map<ll,ll>::iterator it;

void dfs(int u,int fa){

    has[u]=1;

    for(int v:edg[u]){

        dfs(v,u);

        has[u]=(has[u]+has[v]*p)%mod;

    }

    mp[has[u]]++;

}

int main()

{

    int u,v;

    scanf("%d",&n);

    for(int i=1;i<n;i++){

        scanf("%d%d",&u,&v);

        edg[u].push_back(v);

    }

    dfs(1,0);

    ll ans=0;

    for(it =mp.begin();it!=mp.end();it++){

        ans+=(it->second-1)*it->second/2;

    }

    printf("%lld\n",ans);

    return 0;

}

// Optimized solution to the same problem (faster):
#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<vector>

#include<queue>

#include<stack>

#include<cmath>

#include<set>

#include<map>

using namespace std;

#define rep(i,a,n) for (int i=a;i<n;i++)

#define per(i,a,n) for (int i=n-1;i>=a;i--)

#define pb push_back

#define fi first

#define se second

#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;

typedef vector<int> VI;

typedef long long ll;

typedef pair<double,int> PII;

const ll p=998244353;

const ll mod=1e9+7;

const int maxn=1e5+100;

map<ll,int> mp;



struct node

{

    int to,next;

}e[maxn*2];



int tol=0;

int head[maxn];

void add(int u,int v)

{

    e[++tol].to=v,e[tol].next=head[u],head[u]=tol;

}

ll dfs(int u,int f)

{

    ll ans=1;

    for(int i=head[u];i;i=e[i].next)

    {

        int v=e[i].to;

        if(v==f) continue;

        ans=(ans+dfs(v,u)*mod%p)%p;

    }

    mp[ans]++;

    return ans;

}





int main()

{

    int n;

    scanf("%d",&n);

    rep(i,1,n)

    {

        int u,v;

        scanf("%d%d",&u,&v);

        add(u,v),add(v,u);

    }

    mp.clear();

    dfs(1,0);

    ll ans=0;

    map<ll,int>::iterator it;

    for(it=mp.begin();it!=mp.end();it++) ans+=1ll*(it->se)*(it->se-1)/2;

    printf("%lld\n",ans);

    return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):


#include<algorithm>

#include<vector>

 #include<cstring>

#include<string>

#include<iomanip>

#include<cstdio>

#include<stack>

#include<iostream>

#include<map>

#include<queue>

#include<cmath>

using namespace std;

#define sf scanf

#define pf printf

#define mem(a,b) memset(a,b,sizeof(a));

#define rep(i,a,b) for(int i=(a);i<=(b);++i)

#define mod 998244353

#define N 100020

#define M 400020

#define ULL unsigned long long

#define LL long long

#define inf 0x3f3f3f3f

map<ULL,LL>mp;

vector<int>g[N];

ULL dp[N];

LL ans;int n;

const ULL b=1000000007;

void dfs(int u,int fa){

    dp[u]=1;

    for(int i=0;i<g[u].size();++i){

        int v=g[u][i];

        if(v==fa)continue;

        dfs(v,u);

        dp[u]+=dp[v]*b;

    }

    ans+=mp[dp[u]];

    mp[dp[u]]++;

}

int main(){

    sf("%d",&n);

        ans=0;

        rep(i,1,n-1){

            int u,v;sf("%d%d",&u,&v);

            g[u].push_back(v);g[v].push_back(u);

        }

        dfs(1,1);

        cout<<ans<<'\n';

}

// Optimized solution to the same problem (faster):
#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<vector>

#include<queue>

#include<stack>

#include<cmath>

#include<set>

#include<map>

using namespace std;

#define rep(i,a,n) for (int i=a;i<n;i++)

#define per(i,a,n) for (int i=n-1;i>=a;i--)

#define pb push_back

#define fi first

#define se second

#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;

typedef vector<int> VI;

typedef long long ll;

typedef pair<double,int> PII;

const ll p=998244353;

const ll mod=1e9+7;

const int maxn=1e5+100;

map<ll,int> mp;



struct node

{

    int to,next;

}e[maxn*2];



int tol=0;

int head[maxn];

void add(int u,int v)

{

    e[++tol].to=v,e[tol].next=head[u],head[u]=tol;

}

ll dfs(int u,int f)

{

    ll ans=1;

    for(int i=head[u];i;i=e[i].next)

    {

        int v=e[i].to;

        if(v==f) continue;

        ans=(ans+dfs(v,u)*mod%p)%p;

    }

    mp[ans]++;

    return ans;

}





int main()

{

    int n;

    scanf("%d",&n);

    rep(i,1,n)

    {

        int u,v;

        scanf("%d%d",&u,&v);

        add(u,v),add(v,u);

    }

    mp.clear();

    dfs(1,0);

    ll ans=0;

    map<ll,int>::iterator it;

    for(it=mp.begin();it!=mp.end();it++) ans+=1ll*(it->se)*(it->se-1)/2;

    printf("%lld\n",ans);

    return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include<bits/stdc++.h>

using namespace std;

const int N=100005;

typedef long long int LL;





vector<int >ve[N];

map< LL , int >ma;

LL a[N];

LL b=10e2+7;

LL ans;

void dfs(int u)

{

    a[u]=1;

    for(int i=0;i<ve[u].size();i++)

    {

        int v=ve[u][i];

        dfs(v);

        a[u]+=a[v]*b;

    }

    ans+=ma[a[u]];

    ma[a[u]]++;

    return ;

}



int main()

{

    int n;

        cin>>n;

        int u,v;

        for(int i=0;i<n-1;i++)

        {

            cin>>u>>v;

            ve[u].push_back(v);

        }

        ans=0;

        dfs(1);

        cout<<ans<<endl;





    return 0;

}

// Optimized solution to the same problem (faster):
#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<vector>

#include<queue>

#include<stack>

#include<cmath>

#include<set>

#include<map>

using namespace std;

#define rep(i,a,n) for (int i=a;i<n;i++)

#define per(i,a,n) for (int i=n-1;i>=a;i--)

#define pb push_back

#define fi first

#define se second

#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;

typedef vector<int> VI;

typedef long long ll;

typedef pair<double,int> PII;

const ll p=998244353;

const ll mod=1e9+7;

const int maxn=1e5+100;

map<ll,int> mp;



struct node

{

    int to,next;

}e[maxn*2];



int tol=0;

int head[maxn];

void add(int u,int v)

{

    e[++tol].to=v,e[tol].next=head[u],head[u]=tol;

}

ll dfs(int u,int f)

{

    ll ans=1;

    for(int i=head[u];i;i=e[i].next)

    {

        int v=e[i].to;

        if(v==f) continue;

        ans=(ans+dfs(v,u)*mod%p)%p;

    }

    mp[ans]++;

    return ans;

}





int main()

{

    int n;

    scanf("%d",&n);

    rep(i,1,n)

    {

        int u,v;

        scanf("%d%d",&u,&v);

        add(u,v),add(v,u);

    }

    mp.clear();

    dfs(1,0);

    ll ans=0;

    map<ll,int>::iterator it;

    for(it=mp.begin();it!=mp.end();it++) ans+=1ll*(it->se)*(it->se-1)/2;

    printf("%lld\n",ans);

    return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include<bits/stdc++.h>

using namespace std;

const int N=100005;

typedef long long int LL;





vector<int >ve[N];

map< LL , int >ma;

LL a[N];

LL b=17;

LL ans;

void dfs(int u)

{

    a[u]=1;

    for(int i=0;i<ve[u].size();i++)

    {

        int v=ve[u][i];

        dfs(v);

        a[u]+=a[v]*b;

    }

    ans+=ma[a[u]];

    ma[a[u]]++;

    return ;

}



int main()

{

    int n;

        cin>>n;

        int u,v;

        for(int i=0;i<n-1;i++)

        {

            cin>>u>>v;

            ve[u].push_back(v);

        }

        ans=0;

        dfs(1);

        cout<<ans<<endl;





    return 0;

}

// Optimized solution to the same problem (faster):
#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<vector>

#include<queue>

#include<stack>

#include<cmath>

#include<set>

#include<map>

using namespace std;

#define rep(i,a,n) for (int i=a;i<n;i++)

#define per(i,a,n) for (int i=n-1;i>=a;i--)

#define pb push_back

#define fi first

#define se second

#define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;

typedef vector<int> VI;

typedef long long ll;

typedef pair<double,int> PII;

const ll p=998244353;

const ll mod=1e9+7;

const int maxn=1e5+100;

map<ll,int> mp;



struct node

{

    int to,next;

}e[maxn*2];



int tol=0;

int head[maxn];

void add(int u,int v)

{

    e[++tol].to=v,e[tol].next=head[u],head[u]=tol;

}

ll dfs(int u,int f)

{

    ll ans=1;

    for(int i=head[u];i;i=e[i].next)

    {

        int v=e[i].to;

        if(v==f) continue;

        ans=(ans+dfs(v,u)*mod%p)%p;

    }

    mp[ans]++;

    return ans;

}





int main()

{

    int n;

    scanf("%d",&n);

    rep(i,1,n)

    {

        int u,v;

        scanf("%d%d",&u,&v);

        add(u,v),add(v,u);

    }

    mp.clear();

    dfs(1,0);

    ll ans=0;

    map<ll,int>::iterator it;

    for(it=mp.begin();it!=mp.end();it++) ans+=1ll*(it->se)*(it->se-1)/2;

    printf("%lld\n",ans);

    return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>



using namespace std;

 

#define lli long long int

#define REP(i,s,n) for(int i=s;i<n;i++)

#define MOD 1000000007

#define NUM 2520

#define INF (1LL<<50)

#define DEBUG 1

#define mp(a,b) make_pair(a,b)

#define SORT(V) sort(V.begin(),V.end())

#define PI 3.14159265358979



signed main(){



	lli n,m;

	cin>>n>>m;



	vector<lli> x(m),y(m),z(m);

	map<lli,vector<lli>> d;



	REP(i,0,m){

		cin>>x[i]>>y[i]>>z[i];

		x[i]--,y[i]--;

		d[x[i]].push_back(y[i]);

		d[y[i]].push_back(x[i]);

	}



	lli data[100100]={0};

	lli cnt=1;

	REP(i,0,n){

		if(data[i]!=0)continue;



		queue<lli> q;

		q.push(i);

		data[i] = cnt;

		while(q.size()){

			lli top = q.front();

			q.pop();

			for(auto e:d[top]){

				if(data[e]!=0)continue;

				data[e] = cnt;

				q.push(e);

			}

		}

		cnt++;

	}

	cout<<cnt-1<<endl;



	return 0;

}

// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;



#define lli long long int

#define REP(i,s,n) for(int i=s;i<n;i++)

#define MOD 1000000007

#define NUM 2520

#define INF (1LL<<50)

#define DEBUG 0

#define mp(a,b) make_pair(a,b)

#define SORT(V) sort(V.begin(),V.end())

#define PI (3.141592653589794)



typedef struct UnionFind{

	vector<lli> par; //par[i]:iの親番号 par[3] = 2 : 3の親が2

	vector<lli> size;//size[i]:iの大きさ 自分のみのときは1,根っこにsizeを持たせる*/



	UnionFind(int N){/*最初はすべてが根であると初期化*/

		REP(i,0,N){

			par.push_back(i);

			size.push_back(1);

		}

	}



	lli root(lli x){/*データxが属する木の根を再起で得る root(x) = {xの木の根}}*/

		if(par[x] == x)return x;

		return par[x] = root(par[x]);

	}



	void unite(lli x,lli y){/*xとyの併合*/

		lli rx = root(x);

		lli ry = root(y);

		if(rx == ry)return;

		par[rx] = ry; /*xとyの根が同じじゃない: xの根をrxをyの根ryに変更する */

		lli resize = size[rx] + size[ry];

		size[rx] = resize;

		size[ry] = resize;

	}



	bool same(lli x,lli y){

		lli rx = root(x);

		lli ry = root(y);

		return rx == ry;

	}



	lli getSize(lli x){

		lli rx = root(x);

		return size[rx];

	}

};



void func(long long N, long long M, std::vector<long long> X, std::vector<long long> Y, std::vector<long long> Z){

    UnionFind uf(N);



    REP(i,0,M){

        uf.unite(X[i],Y[i]);

    }



    set<lli> s;

    REP(i,0,N){

        s.insert(uf.root(i));

    }



    cout<<s.size()<<endl;

}



int main(){

    // cout << fixed << setprecision(5);



    long long N;

    scanf("%lld",&N);

    long long M;

    scanf("%lld",&M);

    std::vector<long long> X(M);

    std::vector<long long> Y(M);

    std::vector<long long> Z(M);

    for(int i = 0 ; i < M ; i++){

        scanf("%lld",&X[i]);

        scanf("%lld",&Y[i]);

        scanf("%lld",&Z[i]);

        X[i]--,Y[i]--;

    }

    func(N, M, std::move(X), std::move(Y), std::move(Z));

    return 0;

}


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

using namespace std;

struct Fast {Fast() {std::cin.tie(0); ios::sync_with_stdio(false);}} fast;

using ll = long long;

using vi = vector<int>;

using vvi = vector<vi>;

using pii = pair<int, int>;

#define rep(i, n) for (int i = 0; i < (int)(n); i++)

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

#define pb push_back

#define mp make_pair

#define MOD 1000000007





vi G[100004];

vector<bool> seen(100004);





void dfs(int v) {

    seen[v] = true;



    for (auto next : G[v]) {

        if (seen[next]) continue;

        dfs(next);

    }



}



int main() {

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



    rep(i, M) {

        int a, b, c;

        cin >> a >> b >> c;

        G[a].push_back(b); //1-origin

        G[b].push_back(a);

    }



    seen.assign(N + 1, false);



    int ans = 0;



    for (int i = 1; i <= N; i++) {

        if (seen[i]) continue;

        dfs(i);

        ans++;

    }



    cout << ans << endl;



}


// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;

struct Fast {Fast() {std::cin.tie(0); ios::sync_with_stdio(false);}} fast;

using ll = long long;

using vi = vector<int>;

using vvi = vector<vi>;

using pii = pair<int, int>;

#define rep(i, n) for (int i = 0; i < (int)(n); i++)

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

#define pb push_back

#define mp make_pair

#define MOD 1000000007



struct UnionFind {

    vector<int> par, rank;



    UnionFind(int n) : par(n , -1), rank(n, 0) { }

    void init(int n) { par.assign(n, -1), rank.assign(n, 0); }



    int root(int x) {

        if (par[x] == -1) return x;

        else return par[x] = root(par[x]); //ここで経路圧縮

    }



    bool same(int x, int y) {

        return root(x) == root(y);

    }



    bool unite(int x, int y) {

        x = root(x); y = root(y);

        if (x == y) return false;

        if (rank[x] < rank[y]) swap(x, y);

        if (rank[x] == rank[y]) ++rank[x];

        par[y] = x;

        return true;

    }

};





int main() {

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



    UnionFind tree(N);



    rep(i, M) {

        int a, b, c;

        cin >> a >> b >> c;

        tree.unite(a - 1, b - 1);

    }



    int ans = 0;



    rep(i, N) {

        if (tree.par[i] == -1) ans++;

    }



    cout << ans << endl;





}


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <iostream>

#include <algorithm>

#include <iomanip>

#include <string>

#include <vector>

#include <math.h>

#include <queue>

#include <deque>

#include <map>

const int MOD = 1e9+7;

typedef long long ll;

using namespace std;



vector<int> graph[100001];

bool reached[100001];



void dfs(int x){

	reached[x] = true;

	for (auto y : graph[x]){

		if (!reached[y]){

			dfs(y);

		}

	}

	return;

}



int main(){

	int n, m;

	cin >> n >> m;



	for (int i = 0; i < m; i++){

		int x, y, z;

		cin >> x >> y >> z;

		graph[x].push_back(y);

		graph[y].push_back(x);

	}



	int ans = 0;

	for (int i = 1; i <= n; i++){

		if (!reached[i]){

			dfs(i);

			ans++;

		}

	}

	cout << ans << endl;

	return 0;

}


// Optimized solution to the same problem (faster):
#include <iostream>

#include <algorithm>

#include <iomanip>

#include <string>

#include <vector>

#include <math.h>

#include <queue>

#include <deque>

#include <stack>

#include <map>

const int MOD = 1e9+7;

#define PI 3.14159265359

typedef long long ll;

using namespace std;



int P[100010];

int used[100010];



void init(int N){ 

	for (int i = 0; i < N; ++i) P[i] = i;

}



int root(int a){ 

	if (P[a] == a) return a; 

	return (P[a] = root(P[a])); 

}



bool is_same_set(int a, int b){ 

	return root(a) == root(b);

}



void unite(int a, int b){ 

	P[root(a)] = root(b);

}



int main(){

	int n, m;

	cin >> n >> m;

	init(n);

	for (int i = 0; i < m; i++){

		int x, y, z;

		cin >> x >> y >> z;

		unite(x-1, y-1);

	}

	int ans = 0;

	for (int i = 0; i < n; i++){

		if (!used[root(P[i])]){

			used[root(P[i])] = true;

			ans++;

		}

	}

	cout << ans << endl;

	return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

#define cout16 cout << setprecision(16) 

#define rep(i,n) for(int i=0;i<n;i++ )

#define rep2(i,f,n) for(int i=f;i<n;i++ )

#define MAX(A) *max_element(A.begin(),A.end())

#define MIN(A) *min_element(A.begin(),A.end())

#define SORT(A) sort(A.begin(),A.end())

#define REV(A) reverse(A.begin(),A.end())

typedef long long int ll;

using vi = std::vector<int>;

using vvi = std::vector<std::vector<int>>;

using vll = std::vector<ll>;

using vvll = std::vector<std::vector<ll>>;

using P = std::pair<int,int>;

using vp = std::vector<P>;

using namespace std;

#define INF 1001001001

#define LL_INF 1001001001001001001



vi root(100005);

int find(int x){

	if(root[x]==x) return x;

	else return root[x]=find(root[x]);

}

bool same(int x, int y){

	return find(x)==find(y);

}	

void union_tree(int x,int y, int &n_trees){

	if(same(x,y)) return;

	int rt_x = root[x];

	int rt_y = root[y];	

	root[rt_y] = rt_x;

	n_trees--;

}



int main(void) {

	int n,m; cin >> n >> m;

	int n_trees = n;

	rep(i,n) root[i] = i;

	rep(i,m){

		int x,y,z; cin >> x >> y >> z;

		x--; y--;

		union_tree(x,y,n_trees);

	}

	cout << n_trees << endl;

}






// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

#define cout16 cout << setprecision(16) 

#define rep(i,n) for(int i=0;i<n;i++ )

#define rep2(i,f,n) for(int i=f;i<n;i++ )

#define MAX(A) *max_element(A.begin(),A.end())

#define MIN(A) *min_element(A.begin(),A.end())

#define SORT(A) sort(A.begin(),A.end())

#define REV(A) reverse(A.begin(),A.end())

typedef long long int ll;

using vi = std::vector<int>;

using vvi = std::vector<std::vector<int>>;

using vll = std::vector<ll>;

using vvll = std::vector<std::vector<ll>>;

using P = std::pair<int,int>;

using vp = std::vector<P>;

using namespace std;

#define INF 1001001001

#define LL_INF 1001001001001001001



class UnionFindTrees{

	public:

		UnionFindTrees(int _n){

			n = _n;

			root = vector<int>(_n);

			rep(i,_n) root[i] = i;

		}

		int find(int x){

			if(root[x]==x) return x;

			else return root[x]=find(root[x]);			

		}

		bool same(int x, int y){

			return find(x)==find(y);

		}

		void union_tree(int x, int y){

			if(same(x,y)) return;

			int rt_x = root[x];

			int rt_y = root[y];	

			root[rt_y] = rt_x;

			n--;			

		}

		int forest_size(){return n;}

	private:

		vector<int> root;

		int n;



};



int main(void) {

	int n,m; cin >> n >> m;

	UnionFindTrees uft(n);

	rep(i,m){

		int x,y,z; cin >> x >> y >> z;

		x--; y--;

		uft.union_tree(x,y);

	}

	cout << uft.forest_size() << endl;

}






// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

#define ll long long

#define ld long double

#pragma 03

using namespace std;

bool accessed[100005];

vector <int> adjacent[100005];

void dfs(int i){

	if (accessed[i]) return;

	accessed[i] = true;

	for (int j = 0; j<adjacent[i].size(); j++) dfs(adjacent[i][j]);

}

int main(){

	int n, m, x, y, z;

	for (int i = 0; i<100005; i++){

		accessed[i] = false;

		adjacent[i].clear();

	}

	cin >> n >> m;

	for (int i = 0; i<m; i++){

		cin >> x >> y >> z;

		adjacent[x].push_back(y);

		adjacent[y].push_back(x);

	}

	int components = 0;

	for (int i = 1; i<=n; i++){

		if (!accessed[i]){

			dfs(i);

			components++;

		}

	}

	cout << components << endl;

	return 0;

}

// Optimized solution to the same problem (faster):
#include <iostream>

#include<math.h>

#include<vector>

#include<map>

#include<set>

#include<queue>

#include<stack>

#include <algorithm>

#include<string.h>

#define ll long long

#define ull unsigned long long

using namespace std;

/* 

struct edge{

	int to, nxt;

}e[100005];

int head[100005], tot = 1, x, num[100005];

void add(int v, int u)

{

	e[tot].to = u, e[tot].nxt = head[v], head[v] = tot++;

}*/

int dis[100005], k, n, m;

int find(int x)

{

	if (dis[x] != x)	return dis[x] = find(dis[x]);

	return x;

}

void uni(int x, int y)

{

	int xx = find(x);

	int yy = find(y);

	if (xx != yy)

	{

		dis[xx] = yy;

	}

}

signed main() 

{

	while (~scanf("%d%d", &n, &m))

	{

		k = 0; 

		for (int i = 1; i <= n; i++)	dis[i] = i;

		for (int i = 0; i < m; i++)

		{

			int a, b, c;

			scanf("%d%d%d", &a, &b, &c);

			uni(a, b);

		}

		for (int i = 1; i <= n; i++)

			if (find(i) == i)	k++;

		printf("%d\n", k);

	}

}


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 25, M = 1e6 + 25, oo = 1e9+7, mxmsk = 1 << 16;

const long long OO = 1e18+25;

    

#define f first

#define s second



int n, m, u, v,x,cnt, vis[N];

vector <int> g[N];



void visit_it(int node)

{

    if(vis[node]) return;

    vis[node] = 1;

    for(int to : g[node])

    {

        visit_it(to);

    }

}



int main() {

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

    while(m--)

    {

        scanf("%d%d%d",&u,&v,&x);

        g[u].push_back(v);

        g[v].push_back(u);

    }

    for(int i=1; i<=n ;i++)

    {

        if(vis[i]) continue;

        cnt++;

        visit_it(i);

    }

    printf("%d",cnt);

    return 0;

}

// Optimized solution to the same problem (faster):
#include <iostream>

#include<math.h>

#include<vector>

#include<map>

#include<set>

#include<queue>

#include<stack>

#include <algorithm>

#include<string.h>

#define ll long long

#define ull unsigned long long

using namespace std;

/* 

struct edge{

	int to, nxt;

}e[100005];

int head[100005], tot = 1, x, num[100005];

void add(int v, int u)

{

	e[tot].to = u, e[tot].nxt = head[v], head[v] = tot++;

}*/

int dis[100005], k, n, m;

int find(int x)

{

	if (dis[x] != x)	return dis[x] = find(dis[x]);

	return x;

}

void uni(int x, int y)

{

	int xx = find(x);

	int yy = find(y);

	if (xx != yy)

	{

		dis[xx] = yy;

	}

}

signed main() 

{

	while (~scanf("%d%d", &n, &m))

	{

		k = 0; 

		for (int i = 1; i <= n; i++)	dis[i] = i;

		for (int i = 0; i < m; i++)

		{

			int a, b, c;

			scanf("%d%d%d", &a, &b, &c);

			uni(a, b);

		}

		for (int i = 1; i <= n; i++)

			if (find(i) == i)	k++;

		printf("%d\n", k);

	}

}


// Fastest known solution for this problem: