// Slower C++ solution:
#include<iostream>

#include<algorithm>

#include<climits>

#include<vector>

#include<utility>

#include<cmath>

#include<string>

#include<cstring>

#include<map>

#include<queue>

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

#define rep1(i,n) for(int i=1;i<=n;i++)

#define prin(arg) std::cout<<arg<<"\n"

#define prin2(arg1,arg2) std::cout<<arg1<<" "<<arg2<<"\n"

#define fill(arg,n) std::memset(arg,n,sizeof(arg))

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

using std::cin;

typedef long long ll;

typedef std::pair<int,int> pi;

typedef std::pair<int,ll> pil;

typedef std::vector<int> vi;

typedef std::vector<pil> vil;

const int INF=1e+9;

const ll INFLL=1e+17;

int N;

int u[100010],v[100010];

ll w[100010];

vil edge[100010];

ll d[100010];

bool F[100010];

void add_edge(int from,int to,ll cost){

  edge[from].push_back(mp(to,cost));

  edge[to].push_back(mp(from,cost));  

}

void bfs(){

  rep1(i,N) d[i]=INFLL;

  std::queue<pil> q;

  q.push(mp(u[0],0));

  d[u[0]]=0;

  while(!q.empty()){

    pil p=q.front();q.pop();

    int f=p.first;

    ll s=p.second;

    rep(i,edge[f].size()){

      pil pp=edge[f][i];

      int ff=pp.first;

      ll ss=pp.second;

      if(d[ff]==INFLL){

	d[ff]=s+ss;

	q.push(mp(ff,d[ff]));

      }

    }

  }

}

void solve(){

  rep(i,N-1) add_edge(u[i],v[i],w[i]);

  bfs();

  rep1(i,N){

    if(d[i]%2==0) prin(0);

    else prin(1);

  }

}

int main(){

  cin>>N;

  rep(i,N-1) cin>>u[i]>>v[i]>>w[i];

  solve();

  // rep1(i,N) prin2(i,d[i]);

  return 0;

}


// Faster C++ solution for the same problem (measured speedup: 3.4948260259441533x):
#include <iostream>

#include <algorithm>

#include <vector>

#include <string>

#include <utility>

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

#define rep1(i,n) for(int i = 1; i <= n; ++i)

using namespace std;

template<class T>bool chmax(T &a, const T &b) { if(a < b){ a = b; return 1; } return 0; }

template<class T>bool chmin(T &a, const T &b) { if(a > b){ a = b; return 1; } return 0; }

typedef long long ll;

//*******************************************************

// Tree

//*******************************************************



// status of edge

template <typename X>

struct Edge{

  int from;

  int to;

  X cost;



  Edge() = default;



  Edge(int from, int to, X cost) : from(from), to(to), cost(cost) {}

};



//status of node

template <typename X>

struct Node{

  int idx;

  int par;

  X depth;

  vector<Edge<X>> edge;

  

  Node() = default;



  explicit Node(int idx) : idx(idx) {

  }

  

};



// tree

template <typename X>

class Tree{

private:

  int n; // number of node

  vector<Node<X>> node;



  void Init_Node() {

    rep(i,n) node.emplace_back(i);    

  }

  

public:

  Tree() = default;



  explicit Tree(int n) : n(n) {

    Init_Node();

  }



  // no-weight

  Tree(int n, vector<int> a, vector<int> b) : n(n) {

    Init_Node();

    rep(i,n-1) {

      add_edge(a[i], b[i]);

      add_edge(b[i], a[i]);  // indirected edge

    }

  }



  Tree(int n, vector<int> a, vector<int> b, vector<X> c) : n(n) {

    Init_Node();

    rep(i,n-1) {

      add_edge(a[i], b[i], c[i]);

      add_edge(b[i], a[i], c[i]);  // indirected edge

    }

  }  



  void add_edge(int from, int to, X cost = 1) {

    node[from].edge.emplace_back(from, to, cost);

  }



  void DFS_Init(int v, int p, int d) {

    node[v].par = p;

    node[v].depth = d;

    for(auto next: node[v].edge) {

      int w = next.to;

      X cost = next.cost;

      if(w == p) continue;

      DFS_Init(w, v, d + cost);

    }

  }



  // make rooted tree

  void Make_root(int root) {

    DFS_Init(root, -1, 0);

  }



  void Solve() {

    Make_root(0);

    rep(i,n) {

      if(node[i].depth % 2 == 0) cout << 0 << "\n";

      else cout << 1 << "\n";

    }

  }

  

};



int main()

{

  int n; cin >> n;

  vector<int> u(n-1),v(n-1);

  vector<ll> w(n-1);

  rep(i,n-1) {

    cin >> u[i] >> v[i] >> w[i];

    u[i]--; v[i]--;

  }

  Tree<ll> tr(n, u, v, w);

  tr.Solve();

  

  return 0;

}