#line 1 "verify/matrix/LC_matrix_det_mod_2.test.cpp"
#define PROBLEM "https://judge.yosupo.jp/problem/matrix_det_mod_2"
#line 2 "template/template.hpp"
#include <bits/stdc++.h>
using namespace std ;
#line 2 "template/macro.hpp"
#define rep(i, a, b) for (int i = (a); i < (int)(b); i++)
#define rrep(i, a, b) for (int i = (int)(b) - 1; i >= (a); i--)
#define ALL(v) (v).begin(), (v).end()
#define UNIQUE(v) sort(ALL(v)), (v).erase(unique(ALL(v)), (v).end())
#define SZ(v) (int)v.size()
#define MIN(v) *min_element(ALL(v))
#define MAX(v) *max_element(ALL(v))
#define LB(v, x) int(lower_bound(ALL(v), (x)) - (v).begin())
#define UB(v, x) int(upper_bound(ALL(v), (x)) - (v).begin())
#define YN(b) cout << ((b) ? "YES" : "NO") << "\n";
#define Yn(b) cout << ((b) ? "Yes" : "No") << "\n";
#define yn(b) cout << ((b) ? "yes" : "no") << "\n";
#line 6 "template/template.hpp"
#line 2 "template/util.hpp"
using uint = unsigned int ;
using ll = long long int ;
using ull = unsigned long long ;
using i128 = __int128_t ;
using u128 = __uint128_t ;
template < class T >
using priority_queue_asc = priority_queue < T , vector < T > , greater < T >> ;
template < class T , class S = T >
S SUM ( const vector < T >& a ) {
return accumulate ( ALL ( a ), S ( 0 ));
}
template < class T1 , class T2 >
inline bool chmin ( T1 & a , T2 b ) {
if ( a > b ) {
a = b ;
return true ;
}
return false ;
}
template < class T1 , class T2 >
inline bool chmax ( T1 & a , T2 b ) {
if ( a < b ) {
a = b ;
return true ;
}
return false ;
}
template < class T1 , class T2 >
inline bool chmin_opt ( optional < T1 >& a , T2 b ) {
if ( ! a || a > b ) {
a = b ;
return true ;
}
return false ;
}
template < class T1 , class T2 >
inline bool chmax_opt ( optional < T1 >& a , T2 b ) {
if ( ! a || a < b ) {
a = b ;
return true ;
}
return false ;
}
template < class T >
int popcnt ( T x ) {
return __builtin_popcountll ( x );
}
template < class T >
int topbit ( T x ) {
return ( x == 0 ? - 1 : 63 - __builtin_clzll ( x ));
}
template < class T >
int lowbit ( T x ) {
return ( x == 0 ? - 1 : __builtin_ctzll ( x ));
}
#line 8 "template/template.hpp"
#line 2 "template/inout.hpp"
struct Fast {
Fast () {
cin . tie ( nullptr );
ios_base :: sync_with_stdio ( false );
cout << fixed << setprecision ( 15 );
}
} fast ;
ostream & operator << ( ostream & os , __uint128_t x ) {
char buf [ 40 ];
size_t k = 0 ;
while ( x > 0 ) buf [ k ++ ] = ( char )( x % 10 + '0' ), x /= 10 ;
if ( k == 0 ) buf [ k ++ ] = '0' ;
while ( k ) os << buf [ -- k ];
return os ;
}
ostream & operator << ( ostream & os , __int128_t x ) {
return x < 0 ? ( os << '-' << ( __uint128_t )( - x )) : ( os << ( __uint128_t ) x );
}
template < class T , size_t N >
ostream & operator << ( ostream & os , const array < T , N >& a );
template < class T1 , class T2 >
istream & operator >> ( istream & is , pair < T1 , T2 >& p ) {
return is >> p . first >> p . second ;
}
template < class T1 , class T2 >
ostream & operator << ( ostream & os , const pair < T1 , T2 >& p ) {
return os << p . first << " " << p . second ;
}
template < class T >
istream & operator >> ( istream & is , vector < T >& a ) {
for ( auto & v : a ) is >> v ;
return is ;
}
template < class T >
ostream & operator << ( ostream & os , const vector < T >& a ) {
for ( auto it = a . begin (); it != a . end ();) {
os << * it ;
if ( ++ it != a . end ()) os << " " ;
}
return os ;
}
template < class T , size_t N >
ostream & operator << ( ostream & os , const array < T , N >& a ) {
for ( auto it = a . begin (); it != a . end ();) {
os << * it ;
if ( ++ it != a . end ()) os << " " ;
}
return os ;
}
template < class T >
ostream & operator << ( ostream & os , const set < T >& st ) {
os << "{" ;
for ( auto it = st . begin (); it != st . end ();) {
os << * it ;
if ( ++ it != st . end ()) os << "," ;
}
os << "}" ;
return os ;
}
template < class T1 , class T2 >
ostream & operator << ( ostream & os , const map < T1 , T2 >& mp ) {
os << "{" ;
for ( auto it = mp . begin (); it != mp . end ();) {
os << it -> first << ":" << it -> second ;
if ( ++ it != mp . end ()) os << "," ;
}
os << "}" ;
return os ;
}
void in () {}
template < typename T , class ... U >
void in ( T & t , U & ... u ) {
cin >> t ;
in ( u ...);
}
template < class ... T >
void in_zip ( int n , T & ... t ) {
assert ( n >= 0 && (( size ( t ) >= static_cast < size_t > ( n )) && ...));
for ( int i = 0 ; i < n ; i ++ ) in ( t [ i ]...);
}
void out () { cout << " \n " ; }
template < typename T , class ... U , char sep = ' ' >
void out ( const T & t , const U & ... u ) {
cout << t ;
if ( sizeof ...( u )) cout << sep ;
out ( u ...);
}
template < class T , class U >
void out_opt ( const optional < T >& opt , const U & fallback , ostream & os = cout ) {
if ( opt . has_value ())
os << opt . value ();
else
os << fallback ;
os << " \n " ;
}
template < class T , class U >
void out_opt ( const vector < optional < T >>& vec , const U & fallback , ostream & os = cout ) {
for ( auto it = vec . begin (); it != vec . end ();) {
if (( * it ). has_value ())
os << ( * it ). value ();
else
os << fallback ;
if ( ++ it != vec . end ()) os << " " ;
}
os << " \n " ;
}
namespace IO {
template < class T , class ... U >
T read ( U && ... u ) {
T t = T ( forward < U > ( u )...);
in ( t );
return t ;
}
namespace Graph {
vector < vector < int >> unweighted ( int n , int m , bool directed = false , int offset = 1 ) {
vector < vector < int >> g ( n );
for ( int i = 0 ; i < m ; i ++ ) {
int u , v ;
cin >> u >> v ;
u -= offset , v -= offset ;
g [ u ]. push_back ( v );
if ( ! directed ) g [ v ]. push_back ( u );
}
return g ;
}
template < class T >
vector < vector < pair < int , T >>> weighted ( int n , int m , bool directed = false , int offset = 1 ) {
vector < vector < pair < int , T >>> g ( n );
for ( int i = 0 ; i < m ; i ++ ) {
int u , v ;
T w ;
cin >> u >> v >> w ;
u -= offset , v -= offset ;
g [ u ]. push_back ({ v , w });
if ( ! directed ) g [ v ]. push_back ({ u , w });
}
return g ;
}
} // namespace Graph
namespace Tree {
vector < vector < int >> unweighted ( int n , bool directed = false , int offset = 1 ) {
return Graph :: unweighted ( n , n - 1 , directed , offset );
}
template < class T >
vector < vector < pair < int , T >>> weighted ( int n , bool directed = false , int offset = 1 ) {
return Graph :: weighted < T > ( n , n - 1 , directed , offset );
}
vector < vector < int >> rooted ( int n , bool to_root = true , bool to_leaf = true , int offset = 1 ) {
vector < vector < int >> g ( n );
for ( int i = 1 ; i < n ; i ++ ) {
int p ;
cin >> p ;
p -= offset ;
if ( to_root ) g [ i ]. push_back ( p );
if ( to_leaf ) g [ p ]. push_back ( i );
}
return g ;
}
} // namespace Tree
} // namespace IO
#line 10 "template/template.hpp"
#line 2 "template/debug.hpp"
#ifdef LOCAL
#define debug 1
#define show(...) _show(0, #__VA_ARGS__, __VA_ARGS__)
#else
#define debug 0
#define show(...) true
#endif
template < class T >
void _show ( int , T ) {
cerr << '\n' ;
}
template < class T1 , class T2 , class ... T3 >
void _show ( int i , const T1 & a , const T2 & b , const T3 & ... c ) {
for (; a [ i ] != ',' && a [ i ] != '\0' ; i ++ ) cerr << a [ i ];
cerr << ":" << b << " " ;
_show ( i + 1 , a , c ...);
}
#line 2 "matrix/matrix-mod2.hpp"
#line 2 "data-structure/dynamic-bitset.hpp"
struct DynamicBitset {
using u64 = uint64_t ;
struct reference {
DynamicBitset * b ;
int pos ;
reference & operator = ( bool v ) {
b -> set ( pos , v );
return * this ;
}
reference & operator = ( const reference & r ) { return * this = bool ( r ); }
reference & flip () {
b -> flip ( pos );
return * this ;
}
operator bool () const { return b -> test ( pos ); }
};
DynamicBitset () : _n ( 0 ) {}
explicit DynamicBitset ( int n , bool value = false ) : _n ( n ), a ( blocks ( n ), value ? ~ 0ull : 0ull ) {
trim ();
}
int size () const { return _n ; }
bool empty () const { return _n == 0 ; }
bool test ( int pos ) const {
assert ( 0 <= pos && pos < _n );
return ( a [ pos / W ] >> ( pos % W )) & 1ull ;
}
bool operator []( int pos ) const { return test ( pos ); }
reference operator []( int pos ) {
assert ( 0 <= pos && pos < _n );
return reference { this , pos };
}
DynamicBitset & set () {
fill ( a . begin (), a . end (), ~ 0ull );
trim ();
return * this ;
}
DynamicBitset & set ( int pos , bool value = true ) {
assert ( 0 <= pos && pos < _n );
if ( value )
a [ pos / W ] |= 1ull << ( pos % W );
else
a [ pos / W ] &= ~ ( 1ull << ( pos % W ));
return * this ;
}
DynamicBitset & reset () {
fill ( a . begin (), a . end (), 0 );
return * this ;
}
DynamicBitset & reset ( int pos ) { return set ( pos , false ); }
DynamicBitset & flip () {
for ( auto & x : a ) x = ~ x ;
trim ();
return * this ;
}
DynamicBitset & flip ( int pos ) {
assert ( 0 <= pos && pos < _n );
a [ pos / W ] ^= 1ull << ( pos % W );
return * this ;
}
int count () const {
int ret = 0 ;
for ( u64 x : a ) ret += __builtin_popcountll ( x );
return ret ;
}
bool any () const {
for ( u64 x : a )
if ( x ) return true ;
return false ;
}
bool none () const { return ! any (); }
bool all () const { return count () == _n ; }
DynamicBitset & operator &= ( const DynamicBitset & r ) {
assert ( _n == r . _n );
for ( int i = 0 ; i < ( int ) a . size (); i ++ ) a [ i ] &= r . a [ i ];
return * this ;
}
DynamicBitset & operator |= ( const DynamicBitset & r ) {
assert ( _n == r . _n );
for ( int i = 0 ; i < ( int ) a . size (); i ++ ) a [ i ] |= r . a [ i ];
return * this ;
}
DynamicBitset & operator ^= ( const DynamicBitset & r ) {
assert ( _n == r . _n );
for ( int i = 0 ; i < ( int ) a . size (); i ++ ) a [ i ] ^= r . a [ i ];
return * this ;
}
DynamicBitset operator ~ () const {
DynamicBitset ret ( * this );
ret . flip ();
return ret ;
}
DynamicBitset operator & ( const DynamicBitset & r ) const { return DynamicBitset ( * this ) &= r ; }
DynamicBitset operator | ( const DynamicBitset & r ) const { return DynamicBitset ( * this ) |= r ; }
DynamicBitset operator ^ ( const DynamicBitset & r ) const { return DynamicBitset ( * this ) ^= r ; }
DynamicBitset & operator <<= ( int k ) {
assert ( k >= 0 );
if ( k >= _n ) return reset ();
int q = k / W , r = k % W ;
if ( q ) {
for ( int i = ( int ) a . size () - 1 ; i >= 0 ; i -- ) a [ i ] = i >= q ? a [ i - q ] : 0 ;
}
if ( r ) {
for ( int i = ( int ) a . size () - 1 ; i > 0 ; i -- ) a [ i ] = ( a [ i ] << r ) | ( a [ i - 1 ] >> ( W - r ));
a [ 0 ] <<= r ;
}
trim ();
return * this ;
}
DynamicBitset & operator >>= ( int k ) {
assert ( k >= 0 );
if ( k >= _n ) return reset ();
int q = k / W , r = k % W ;
if ( q ) {
for ( int i = 0 ; i < ( int ) a . size (); i ++ ) a [ i ] = i + q < ( int ) a . size () ? a [ i + q ] : 0 ;
}
if ( r ) {
for ( int i = 0 ; i + 1 < ( int ) a . size (); i ++ ) a [ i ] = ( a [ i ] >> r ) | ( a [ i + 1 ] << ( W - r ));
a . back () >>= r ;
}
return * this ;
}
DynamicBitset operator << ( int k ) const { return DynamicBitset ( * this ) <<= k ; }
DynamicBitset operator >> ( int k ) const { return DynamicBitset ( * this ) >>= k ; }
bool operator == ( const DynamicBitset & r ) const { return _n == r . _n && a == r . a ; }
bool operator != ( const DynamicBitset & r ) const { return ! ( * this == r ); }
int find_first () const { return find_next ( - 1 ); }
int find_next ( int pos ) const {
assert ( - 1 <= pos && pos < _n );
int i = pos + 1 ;
if ( i >= _n ) return _n ;
int b = i / W ;
u64 x = a [ b ] & ( ~ 0ull << ( i % W ));
while ( true ) {
if ( x ) {
int ret = b * W + __builtin_ctzll ( x );
return ret < _n ? ret : _n ;
}
if ( ++ b == ( int ) a . size ()) return _n ;
x = a [ b ];
}
}
string to_string () const {
string s ( _n , '0' );
for ( int i = 0 ; i < _n ; i ++ )
if ( test ( i )) s [ _n - 1 - i ] = '1' ;
return s ;
}
private:
static constexpr int W = 64 ;
int _n ;
vector < u64 > a ;
static int blocks ( int n ) { return ( n + W - 1 ) / W ; }
void trim () {
if ( _n == 0 ) return ;
int r = _n % W ;
if ( r ) a . back () &= ( 1ull << r ) - 1 ;
}
};
/**
* @brief Dynamic Bitset
* @docs docs/data-structure/dynamic-bitset.md
*/
#line 4 "matrix/matrix-mod2.hpp"
struct MatrixMod2 {
using BS = DynamicBitset ;
int h , w ;
vector < BS > a ;
MatrixMod2 () : h ( 0 ), w ( 0 ) {}
MatrixMod2 ( int n ) : h ( n ), w ( n ), a ( n , BS ( n )) {}
MatrixMod2 ( int h_ , int w_ ) : h ( h_ ), w ( w_ ), a ( h , BS ( w )) {}
bool get ( int i , int j ) const { return a [ i ][ j ]; }
void set ( int i , int j , bool v = true ) { a [ i ]. set ( j , v ); }
void add ( int i , int j , bool v = true ) {
if ( v ) a [ i ]. flip ( j );
}
void sub ( int i , int j , bool v = true ) { add ( i , j , v ); }
static MatrixMod2 id ( int n ) {
MatrixMod2 mat ( n );
for ( int i = 0 ; i < n ; i ++ ) mat . set ( i , i );
return mat ;
}
MatrixMod2 & operator += ( const MatrixMod2 & r ) {
assert ( h == r . h && w == r . w );
for ( int i = 0 ; i < h ; i ++ ) a [ i ] ^= r . a [ i ];
return * this ;
}
MatrixMod2 & operator -= ( const MatrixMod2 & r ) { return * this += r ; }
MatrixMod2 operator + ( const MatrixMod2 & r ) const { return MatrixMod2 ( * this ) += r ; }
MatrixMod2 operator - ( const MatrixMod2 & r ) const { return MatrixMod2 ( * this ) -= r ; }
MatrixMod2 operator * ( const MatrixMod2 & r ) const {
if ( w <= 256 ) return multiply_sparse ( r );
return multiply_four_russians ( r );
}
MatrixMod2 multiply_sparse ( const MatrixMod2 & r ) const {
assert ( w == r . h );
MatrixMod2 ret ( h , r . w );
for ( int i = 0 ; i < h ; i ++ )
for ( int k = a [ i ]. find_first (); k < w ; k = a [ i ]. find_next ( k ))
ret . a [ i ] ^= r . a [ k ];
return ret ;
}
MatrixMod2 multiply_four_russians ( const MatrixMod2 & r , int block = 0 ) const {
assert ( w == r . h );
if ( block == 0 ) block = four_russians_block_size ( w );
assert ( block > 0 );
MatrixMod2 ret ( h , r . w );
for ( int l = 0 ; l < w ; l += block ) {
int len = min ( block , w - l );
vector < BS > table ( 1 << len , BS ( r . w ));
for ( int mask = 1 ; mask < ( 1 << len ); mask ++ ) {
int b = __builtin_ctz ( mask );
table [ mask ] = table [ mask ^ ( 1 << b )];
table [ mask ] ^= r . a [ l + b ];
}
for ( int i = 0 ; i < h ; i ++ ) {
int mask = 0 ;
for ( int j = 0 ; j < len ; j ++ )
if ( get ( i , l + j )) mask |= 1 << j ;
ret . a [ i ] ^= table [ mask ];
}
}
return ret ;
}
static int four_russians_block_size ( int n ) {
if ( n <= 512 ) return 4 ;
if ( n <= 1024 ) return 5 ;
if ( n <= 2048 ) return 6 ;
return 7 ;
}
MatrixMod2 & operator *= ( const MatrixMod2 & r ) { return * this = * this * r ; }
MatrixMod2 pow ( long long n ) const {
assert ( h == w );
MatrixMod2 ret = id ( h );
MatrixMod2 mat ( * this );
while ( n > 0 ) {
if ( n & 1 ) ret *= mat ;
mat *= mat ;
n >>= 1 ;
}
return ret ;
}
int rank () const {
MatrixMod2 mat ( * this );
int r = 0 ;
for ( int c = 0 ; c < w && r < h ; c ++ ) {
int p = r ;
while ( p < h && ! mat . get ( p , c )) p ++ ;
if ( p == h ) continue ;
mat . swap_row ( r , p );
for ( int i = 0 ; i < h ; i ++ )
if ( i != r && mat . get ( i , c )) mat . add_row ( i , r );
r ++ ;
}
return r ;
}
int det () const {
assert ( h == w );
MatrixMod2 mat ( * this );
for ( int c = 0 ; c < w ; c ++ ) {
int p = c ;
while ( p < h && ! mat . get ( p , c )) p ++ ;
if ( p == h ) return 0 ;
mat . swap_row ( c , p );
for ( int i = c + 1 ; i < h ; i ++ ) {
if ( mat . get ( i , c )) mat . add_row ( i , c );
}
}
return 1 ;
}
optional < MatrixMod2 > inv () const {
assert ( h == w );
MatrixMod2 mat ( * this );
MatrixMod2 imat = id ( h );
for ( int c = 0 ; c < w ; c ++ ) {
int p = c ;
while ( p < h && ! mat . get ( p , c )) p ++ ;
if ( p == h ) return nullopt ;
mat . swap_row ( c , p );
imat . swap_row ( c , p );
for ( int i = 0 ; i < h ; i ++ ) {
if ( i == c || ! mat . get ( i , c )) continue ;
mat . add_row ( i , c );
imat . add_row ( i , c );
}
}
return imat ;
}
void swap_row ( int i , int j ) {
if ( i == j ) return ;
swap ( a [ i ], a [ j ]);
}
void add_row ( int i , int j ) { a [ i ] ^= a [ j ]; }
void set_row ( int i , const string & s ) {
assert (( int ) s . size () == w );
a [ i ]. reset ();
for ( int j = 0 ; j < w ; j ++ ) {
assert ( s [ j ] == '0' || s [ j ] == '1' );
if ( s [ j ] == '1' ) a [ i ]. set ( j );
}
}
string row_string ( int i ) const {
string s ( w , '0' );
for ( int j = 0 ; j < w ; j ++ )
if ( get ( i , j )) s [ j ] = '1' ;
return s ;
}
friend ostream & operator << ( ostream & os , const MatrixMod2 & mat ) {
for ( int i = 0 ; i < mat . h ; i ++ ) {
os << mat . row_string ( i );
if ( i + 1 < mat . h ) os << " \n " ;
}
return os ;
}
};
/**
* @brief Matrix Mod 2
* @docs docs/matrix/matrix-mod2.md
*/
#line 5 "verify/matrix/LC_matrix_det_mod_2.test.cpp"
int main () {
int n ;
in ( n );
MatrixMod2 a ( n );
string s ;
rep ( i , 0 , n ) {
in ( s );
a . set_row ( i , s );
}
out ( a . det ());
}