#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define all(obj) (obj).begin(), (obj).end() #define range(i, l, r) for(int i=l;i>1)|y)) #define bit_kth(i, k) ((i >> k)&1) #define bit_highest(i) (i?63-__builtin_clzll(i):-1) #define bit_lowest(i) (i?__builtin_ctzll(i):-1) using ll = long long; using ld = long double; using ul = uint64_t; using namespace std; template std::ostream &operator<<(std::ostream &dest, const std::pair &p){ dest << p.first << ' ' << p.second; return dest; } template std::ostream &operator<<(std::ostream &dest, const std::vector> &v){ int sz = v.size(); if(sz==0) return dest; for(int i=0;i std::ostream &operator<<(std::ostream &dest, const std::vector &v){ int sz = v.size(); if(sz==0) return dest; for(int i=0;i std::ostream &operator<<(std::ostream &dest, const std::array &v){ if(sz==0) return dest; for(int i=0;i std::ostream &operator<<(std::ostream &dest, const std::set &v){ for(auto itr=v.begin();itr!=v.end();){ dest << *itr; itr++; if(itr!=v.end()) dest << ' '; } return dest; } template std::ostream &operator<<(std::ostream &dest, const std::map &v){ for(auto itr=v.begin();itr!=v.end();){ dest << '(' << itr->first << ", " << itr->second << ')'; itr++; if(itr!=v.end()) dest << '\n'; } return dest; } template vector make_vec(size_t sz, T val){return std::vector(sz, val);} template auto make_vec(size_t sz, Tail ...tail){ return std::vector(tail...))>(sz, make_vec(tail...)); } template vector read_vec(size_t sz){ std::vector v(sz); for(int i=0;i> v[i]; return v; } template auto read_vec(size_t sz, Tail ...tail){ auto v = std::vector(tail...))>(sz); for(int i=0;i(tail...); return v; } template bool mul_overflow(T a, T b){ static T INF = numeric_limits::max(); return (INF / a) < b; } template bool add_overflow(T a, T b){ static T INF = numeric_limits::max(); return (INF - a) < b; } template bool chmax(T &a, const T b){ if(a >= b) return false; a = b; return true; } template bool chmin(T &a, const T b){ if(a <= b) return false; a = b; return true; } template T max(vector &v, int l=0, int r = -1){ return *std::max_element(v.begin()+l, v.begin()+(r==-1?v.size():r)); } template T min(vector &v, int l=0, int r = -1){ return *std::min_element(v.begin()+l, v.begin()+(r==-1?v.size():r)); } template int argmax(vector &v, int l=0, int r=-1){ T res = max(v, l, r); if(r==-1) r = v.size(); for(int i=l;i int argmin(vector &v, int l=0, int r=-1){ T res = min(v, l, r); if(r==-1) r = v.size(); for(int i=l;i>= __builtin_ctzll(a); do{ b >>= __builtin_ctzll(b); if(a > b) std::swap(a, b); b -= a; }while(b); return (a << shift); } ll lcm(ll a, ll b){ return (a / gcd(a, b)) * b; } ll llpow(ll a, ll b){ ll ret = 1, mul = a; while(b){ if(b&1) ret *= mul; mul *= mul; b >>= 1; } return ret; } template ll mpow(ll a, ll b){ int ret = (MOD==1?0:1), mul = a % MOD; while(b){ if(b&1) ret = (ll(ret) * mul) % MOD; mul = (ll(mul) * mul) % MOD; b >>= 1; } return ret; } ll mpow(ll a, ll b, ll MOD){ int ret = (MOD==1?0:1), mul = a % MOD; while(b){ if(b&1) ret = (ll(ret) * mul) % MOD; mul = (ll(mul) * mul) % MOD; b >>= 1; } return ret; } int centroid_search(int now, int from, const vector> &G, const vector &is_centroid, vector &size, int th, vector &parent){ int c = -1; parent[now] = from; size[now] = 1; for(int to:G[now]){ if(to==from||is_centroid[to]) continue; int tmp_c = centroid_search(to, now, G, is_centroid, size, th, parent); size[now] += size[to]; if(tmp_c!=-1) c = tmp_c; } if(c==-1&&size[now]>th) return now; return c; } tuple>, int, vector, vector> centroid_decomposition(const vector> &G){ int n = G.size(), root; vector is_centroid(n, false); vector size(n), parent(n), depth(n), inner_parent(n); vector> g(n); queue> q; q.push({0, -1, n}); while(!q.empty()){ auto [now, par, subtree_size] = q.front(); q.pop(); if(is_centroid[now]) continue; int c = centroid_search(now, -1, G, is_centroid, size, subtree_size/2, inner_parent); parent[c] = par; depth[c] = (par==-1?0:depth[par] + 1); if(par==-1) root = c; else g[par].push_back(c); is_centroid[c] = true; for(int to:G[c]){ if(is_centroid[to]) continue; while(inner_parent[to]!=-1&&inner_parent[to]!=c) to = inner_parent[to]; q.push({to, c, size[to]>size[c]?size[to]-size[c]:size[to]}); } } return {g, root, parent, depth}; } template struct rbtree_map{ private: struct node{ K key; int red, sz; T val; node *par, *ch[2]; node(): red(0), sz(0){} node(K key, T val):key(key), red(1), sz(1), val(val){ par = ch[0] = ch[1] = nullptr; } }; inline void eval(node *v){ if(v==nil) return; v->sz = 1 + v->ch[0]->sz + v->ch[1]->sz; } void rec_eval(node *v){ if(v==nil) v = v->par; while(v!=nil){ eval(v); v = v->par; } } void rotate(node *x, int d){ int e = d^1; node *y = x->ch[e]; x->ch[e] = y->ch[d]; eval(x); if(y->ch[d]!=nil) y->ch[d]->par = x; y->par = x->par; if(x->par==nil) root = y; else if(x==x->par->ch[d]) x->par->ch[d] = y, eval(x->par); else x->par->ch[e] = y, eval(x->par); y->ch[d] = x; eval(y); x->par = y; } node *minimum(node* x){ while(x->ch[0]!=nil) x = x->ch[0]; return x; } void transplant(node *u, node *v){ if(u->par==nil) root = v; else if(u==u->par->ch[0]) u->par->ch[0] = v; else u->par->ch[1] = v; v->par = u->par; } void insert_update(node *z){ node *y; while(z->par->red){ int d = (z->par==z->par->par->ch[0]?0:1), e = d^1; y = z->par->par->ch[e]; if(y->red){ z->par->red = 0; y->red = 0; z->par->par->red = 1; z = z->par->par; }else if(z==z->par->ch[e]){ z = z->par; rotate(z,d); }else{ z->par->red = 0; z->par->par->red = 1; rotate(z->par->par,e); } } root->red = 0; } node *build(int l, int r, int d, node *parent, const vector> &v){ int mid = (r+l)/2; node *now = new node(v[mid].first, v[mid].second); now->red = d; now->par = parent; if(mid>l) now->ch[0] = build(l, mid, d^1, now, v); else now->ch[0] = nil; if(r>mid+1) now->ch[1] = build(mid+1, r, d^1, now, v); else now->ch[1] = nil; eval(now); return now; } public: node *root, *nil; rbtree_map(){ nil = new node(); root = nil->par = nil->ch[0] = nil->ch[1] = nil; } rbtree_map(vector> v){ nil = new node(); sort(v.begin(), v.end()); root = build(0, v.size(), 0, nil, v); } void print(){ print(root, 0, 0); } int size(){ return root->sz; } node *find(K key){ node *x = root; while(x != nil){ if(key != x->key) x = x->ch[key > x->key]; else return x; } return x; } node *insert(K key, T val){ node *x = root, *y = nil; while(x!=nil){ y = x; if(key != x->key) x = x->ch[key > x->key]; else return x; } node *z = new node(key, val); z->par = y; if(y==nil) root = z; else y->ch[z->key >= y->key] = z, eval(y); z->ch[0] = z->ch[1] = nil; insert_update(z); nil->par = nil->ch[0] = nil->ch[1] = nil; rec_eval(z); return z; } void erase(node *z){ if(z==nil) return; node *y = z, *x; int prevy = y->red; if(z->ch[0]==nil){ x = z->ch[1]; transplant(z, z->ch[1]); rec_eval(x); }else if(z->ch[1]==nil){ x = z->ch[0]; transplant(z, z->ch[0]); rec_eval(x); }else{ y = minimum(z->ch[1]); prevy = y->red; x = y->ch[1]; if(y->par==z){ x->par = y; transplant(z, y); y->ch[0] = z->ch[0]; y->ch[0]->par = y; y->red = z->red; rec_eval(y); }else{ transplant(y, y->ch[1]); node *tmp = y->par; y->ch[1] = z->ch[1]; y->ch[1]->par = y; transplant(z, y); y->ch[0] = z->ch[0]; y->ch[0]->par = y; y->red = z->red; rec_eval(tmp); } } if(!prevy) x->red = 0; delete z; nil->par = nil->ch[0] = nil->ch[1] = nil; } void erase(K key){ erase(find(key)); } node *lower_bound(K key){ node *x = root, *y; while(x != nil){ if(x->key>=key) y = x; if(key != x->key) x = x->ch[key > x->key]; else return x; } return y; } node *upper_bound(K key){ node *x = root, *y; while(x != nil){ if(x->key>key) y = x; x = x->ch[key >= x->key]; } return y; } node *reverse_lower_bound(K key){ node *x = root, *y; while(x != nil){ if(x->key<=key) y = x; if(key != x->key) x = x->ch[key > x->key]; else return x; } return y; } node *reverse_upper_bound(K key){ node *x = root, *y; while(x != nil){ if(x->keych[key > x->key]; } return y; } int under_count(K key){ node *x = root; int ret = 0; while(x != nil){ if(x->key<=key) ret += (x->key!=key) + x->ch[0]->sz; if(key != x->key) x = x->ch[key > x->key]; else return ret; } return ret; } node *kth_element(int k){ if(root->sz<=k) return nil; node *x = root; while(x!=nil){ int lsz = x->ch[0]->sz; if(k==lsz) return x; if(lszch[1]; else x = x->ch[0]; } return nil; } }; int main(){ auto set_map = [](rbtree_map &mp, int a, int b, int c)->void{ auto node = mp.find(ll(a)*1000000000+b); if(node==mp.nil) mp.insert(ll(a)*1000000000+b, c); }; auto add_map = [](rbtree_map &mp, int a, int b, int c)->void{ auto node = mp.find(ll(a)*1000000000+b); if(node==mp.nil) mp.insert(ll(a)*1000000000+b, c); else node->val += c; }; auto get_map = [](rbtree_map &mp, int a, int b)->int{ auto node = mp.find(ll(a)*1000000000 + b); if(node==mp.nil) return 0; return node->val; }; cin.tie(nullptr); ios::sync_with_stdio(false); //各重心ノードcでv-cパスに含まれる色を持っておく //cでマージする時、同じ2色, 1色とその色を含む2色, 同じ1色をマージ int n, m;std::cin >> n >> m; vector> G(n); rbtree_map color; range(i, 0, n-1){ int a, b, c;std::cin >> a >> b >> c; a--, b--; G[a].push_back(b); G[b].push_back(a); set_map(color, a, b, c); set_map(color, b, a, c); } auto [g, root, parent, depth] = centroid_decomposition(G); //cから初めて色が2色以下の間探す ll ans = 0; ll add = 0; for(int i=0;i ALL; ll O = 0; vector> subtree; vector Ocount; for(int ch:G[i]){ if(depth[ch]<=depth[i]) continue; subtree.push_back(rbtree_map()); Ocount.push_back(0); int idx = subtree.size() - 1; queue> q; q.push(tuple{ch, i, get_map(color, i, ch), 0}); while(!q.empty()){ auto [now, from, c1, c2] = q.front(); q.pop(); if(c2==0) O++, Ocount[idx]++; add_map(ALL, c1, c2, 1); add_map(ALL, c2, c1, 1); add_map(subtree[idx], c1, c2, 1); add_map(subtree[idx], c2, c1, 1); for(auto nxt:G[now]){ if(nxt==from||depth[nxt]<=depth[i]) continue; int ecolor = get_map(color, now, nxt); if(c1==ecolor) q.push({nxt, now, c1, c2}); else if(c2==0||c2==ecolor) q.push({nxt, now, c1, ecolor}); } } } for(int j=0;jkey / 1000000000; int c2 = itr->key % 1000000000; if(c1==0||c1val; if(c2==0){ int except_c1_all = O - get_map(ALL, c1, c2); int except_c1_j = Ocount[j] - get_map(subtree[j], c1, c2); ans += ll(except_c1_all - except_c1_j) * count; }else{ ans += count * 2;//2-0 ans += ll(get_map(ALL, c1, c2) - count) * count; int c1_all = get_map(ALL, c1, 0); int c1_j = get_map(subtree[j], c1, 0); int c2_all = get_map(ALL, c2, 0); int c2_j = get_map(subtree[j], c2, 0); add += ll(c1_all - c1_j) * count; add += ll(c2_all - c2_j) * count; } } } } //std::cout << add << '\n'; std::cout << (ans / 2) + add << '\n'; }