#include<iostream>
#include<map>
#include <unordered_map>
using namespace std;
using ll = long long;


/*
大数据量场景(如 n=1e5、1e6,节点数极多):
unordered_map的平均 O (1) 操作效率会显著高于map的 O (log k);
map的 O (log k) 会随着节点数增加,耗时呈对数级增长,累积下来会带来明显的耗时增加,甚至可能超出题目时间限制导致超时;
本题语法上map可直接替换unordered_map,但时间复杂度会大大提升

本题的节点 ID 可能达到1e9量级(离散且范围极大,这正是使用unordered_map最主要原因),如果使用普通数组存储UF结构体:(vector也不适合这种离散ID,开销太大)
数组要求连续内存空间且预分配固定大小,1e9量级的数组根本无法分配(内存容量远远不足,会直接触发内存溢出错误);
而unordered_map是动态哈希表,只存储实际出现过的节点,不会为未出现的节点分配任何内存。
且每组测试结束后,unordered_map都可自动销毁
*/



// 保留原结构体,新增rel字段(记录与根的关系:0=朋友,1=敌人)
// 算法思想:扩展普通并查集,通过额外的关系字段维护节点间的对立/同类关系(种类并查集核心)
struct UF {
    ll id;            // 节点自身ID(保留原变量名)
    UF* root;         // 节点的父节点/根节点指针(保留原变量名)
    ll rank;          // 节点所在树的秩(用于按秩合并,优化时间复杂度,保留原变量名)
    ll rel;           // 新增核心字段:记录当前节点与它的根节点之间的关
    // 0 = 当前节点与根节点是「朋友/同类」;1 = 当前节点与根节点是「敌人/异类」
};

// 带路径压缩的Root函数,同时更新rel(维护节点与根的关系,种类并查集的核心优化)
// 入参:unordered_map<ll, UF>& a - 存储离散节点的并查集(适配大数据范围,避免数组溢出)
// 入参:ll x - 要查找根节点的目标节点ID
// 返回值:UF* - 目标节点x的最终根节点指针
UF* Root(unordered_map<ll, UF>& a, ll x) {
    // 递归终止条件:当前节点的根节点是它自身(说明找到最终根节点)
    if (a[x].root != &a[x]) {
        // 步骤1:保存当前节点的旧根节点(路径压缩前的父节点),用于后续更新关系
        UF* old_root = a[x].root;
        
        // 步骤2:递归查找旧根节点的最终根节点,并完成路径压缩(普通并查集路径压缩逻辑)
        // 递归后,old_root->root 会指向最终根节点,同时old_root->rel会更新为old_root到最终根的关系
        a[x].root = Root(a, old_root->id);
        
        // 步骤3:核心!路径压缩时,更新当前节点x到最终根节点的关系(关系传递性)
        // 逻辑推导:x -> 最终根 的关系 = x -> old_root 的关系 ^ old_root -> 最终根 的关系
        // 异或^适用原因:二值关系(0/1)的传递,满足「朋友的朋友是朋友,朋友的敌人是敌人,敌人的敌人是朋友」
        a[x].rel ^= old_root->rel;
    }
    // 返回最终根节点,此时x已直接指向最终根,且x->rel已更新为x到最终根的正确关系
    return a[x].root;
}

int main() {
    int T;
    cin >> T;
    // 多组测试用例,每组用例独立处理(避免不同用例间的节点状态干扰)
    while (T--) {
        ll n;
        cin >> n;
        // 保留原变量名a,改为unordered_map适配1e9量级的离散节点(解决全局数组内存不足问题)
        // 键:节点ID(ll类型),值:对应的UF结构体(存储节点的并查集状态和关系)
        unordered_map<ll, UF> a;
        bool ok = true; // 标记当前测试用例是否存在矛盾关系(初始化为无矛盾)

        // 处理当前测试用例的n条关系指令
        for (ll i = 0; i < n; i++) {
            ll ai, bi, c;
            cin >> ai >> bi >> c;
            // 指令含义:ai 和 bi 的关系为 c(c=1:朋友/同类;c=0:敌人/异类)

            // 步骤1:初始化节点ai(若未在当前并查集中存在)
            if (a.find(ai) == a.end()) {
                // 初始化规则:
                // 1. 节点ID为自身ai
                // 2. 初始根节点指向自身(普通并查集初始化规则)
                // 3. 初始秩为1(单个节点构成的树,高度/规模为1)
                // 4. 初始关系rel=0(自身与自身必然是朋友/同类,无对立关系)
                a[ai] = { ai, &a[ai], 1, 0 };
            }
            // 步骤2:初始化节点bi(若未在当前并查集中存在),规则同ai
            if (a.find(bi) == a.end()) {
                a[bi] = { bi, &a[bi], 1, 0 };
            }

            // 步骤3:查找ai和bi的最终根节点(同时完成路径压缩和关系更新)
            UF* ra = Root(a, ai); // ra:ai的最终根节点
            UF* rb = Root(a, bi); // rb:bi的最终根节点

            // 情况1:ai和bi已经在同一个集合中(根节点相同),需要检查关系是否矛盾
            if (ra == rb) {
                // 核心逻辑:计算ai和bi的实际关系,与输入的c对比,判断是否矛盾
                // 1. 实际关系推导:ai 和 bi 的关系 = (ai到根的关系) ^ (bi到根的关系)
                // 2. 期望关系:输入c=1(朋友)对应期望关系为0,c=0(敌人)对应期望关系为1
                //    因此期望关系可表示为 1 - c(完成c到实际关系值的转换)
                ll expected_rel = 1 - c; // 转换输入c为对应的二值关系期望
                ll actual_rel = a[ai].rel ^ a[bi].rel; // 计算ai和bi的实际关系

                // 若实际关系与期望关系不一致,说明存在矛盾,标记为false
                if (actual_rel != expected_rel) {
                    ok = false;
                }
            }
            // 情况2:ai和bi不在同一个集合中(根节点不同),需要合并两个集合并维护关系
            else {
                // 步骤1:按秩合并(普通并查集优化,降低树的高度,保证近似O(1)的操作复杂度)
                // 逻辑:将秩小的树合并到秩大的树的根节点下,减少后续路径压缩的开销
                if (ra->rank < rb->rank) {
                    swap(ra, rb);   // 交换根节点,确保ra始终是秩更大的根节点
                    swap(ai, bi);   // 同步交换节点,保证后续关系计算的一致性
                }

                // 步骤2:合并两个集合(将rb的根节点指向ra,完成树的合并)
                rb->root = ra;

                // 步骤3:核心!计算合并后rb(原小树的根)到ra(原大树的根)的关系(rel)
                // 逻辑推导:要保证ai和bi的关系符合输入c,需满足:
                // rb->rel = (ai到ra的关系) ^ (bi到rb的关系) ^ (ai和bi的期望关系)
                // 其中 ai和bi的期望关系为 1 - c,最终化简为如下公式
                rb->rel = a[ai].rel ^ a[bi].rel ^ (1 - c);

                // 步骤4:若两个树的秩相等,合并后大树的秩+1(维护秩的正确性,用于后续按秩合并)
                if (ra->rank == rb->rank) {
                    ra->rank++;
                }
            }
        }

        // 输出当前测试用例的结果:无矛盾输出YES,有矛盾输出NO
        cout << (ok ? "YES" : "NO") << endl;
    }
    return 0;
}