結果
問題 | No.2595 Parsing Challenge |
ユーザー | 👑 p-adic |
提出日時 | 2023-12-24 11:05:36 |
言語 | C++17 (gcc 12.3.0 + boost 1.83.0) |
結果 |
RE
|
実行時間 | - |
コード長 | 35,460 bytes |
コンパイル時間 | 7,286 ms |
コンパイル使用メモリ | 312,100 KB |
実行使用メモリ | 18,616 KB |
最終ジャッジ日時 | 2024-09-27 13:40:27 |
合計ジャッジ時間 | 20,058 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge3 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | RE | - |
testcase_01 | RE | - |
testcase_02 | RE | - |
testcase_03 | RE | - |
testcase_04 | RE | - |
testcase_05 | RE | - |
testcase_06 | RE | - |
testcase_07 | RE | - |
testcase_08 | RE | - |
testcase_09 | RE | - |
testcase_10 | RE | - |
testcase_11 | RE | - |
testcase_12 | RE | - |
testcase_13 | RE | - |
testcase_14 | RE | - |
testcase_15 | RE | - |
testcase_16 | RE | - |
testcase_17 | RE | - |
testcase_18 | RE | - |
testcase_19 | RE | - |
testcase_20 | RE | - |
testcase_21 | RE | - |
testcase_22 | RE | - |
testcase_23 | RE | - |
testcase_24 | RE | - |
testcase_25 | RE | - |
testcase_26 | RE | - |
testcase_27 | RE | - |
testcase_28 | RE | - |
testcase_29 | RE | - |
testcase_30 | TLE | - |
testcase_31 | -- | - |
testcase_32 | -- | - |
testcase_33 | -- | - |
testcase_34 | -- | - |
testcase_35 | -- | - |
testcase_36 | -- | - |
testcase_37 | -- | - |
testcase_38 | -- | - |
testcase_39 | -- | - |
testcase_40 | -- | - |
testcase_41 | -- | - |
testcase_42 | -- | - |
testcase_43 | -- | - |
testcase_44 | -- | - |
testcase_45 | -- | - |
testcase_46 | -- | - |
testcase_47 | -- | - |
testcase_48 | -- | - |
testcase_49 | -- | - |
testcase_50 | -- | - |
testcase_51 | -- | - |
testcase_52 | -- | - |
testcase_53 | -- | - |
testcase_54 | -- | - |
testcase_55 | -- | - |
testcase_56 | -- | - |
testcase_57 | -- | - |
testcase_58 | -- | - |
testcase_59 | -- | - |
ソースコード
#ifdef INCLUDE_MAIN using Func = T2<bigint>; // 重さでバランス取ると重さの探索処理で逆に遅くなりそうなので1次多項式の乗算と同様に隣接2個ずつの分割統治。 // 1次多項式の乗算と違って重さが偏っている時にO(N(log N)^2)になっているか自信がない。TLEしたらここを改善すべきかも。 // ACL使っているから畳み込みを1から見直さなくていいのがいいね! Func& Prod( list<Func>& a ) { auto end = a.end(); while( a.size() > 1 ){ auto itr = a.begin(); while( itr != end ){ Func& f0 = *itr; itr++; if( itr != end ){ Func& f1 = *itr; f0.second += f1.second *= f0.first; f0.first *= f1.first; itr = a.erase( itr ); } } } return a.front(); } CEXPR( uint , leaf_size , 2 ); inline void Debug( ... ){} // inline void Debug( const string& s , const int& i , const string& func ){ cerr << func << ": s[" << i << "] == " << s[i] << endl; } // inline void Debug( const string& s , const vector<vector<int>>& child , const vector<Func>& val , const int& v ){ if( child[v].size() == leaf_size ){ cerr << "Evaluate: s[" << child[v][0] << ":" << child[v][1] << "] == " << s.substr( child[v][0] , child[v][1] - child[v][0] + 1 ) << " , val[" << v << "] == (" << val[v].first << "," << val[v].second << ")" << endl; } else { cerr << "Evaluate: s[" << child[v][0] << "] == " << s[child[v][0]] << " , val[" << v << "] == (" << val[v].first << "," << val[v].second << ")" << endl; } } inline bool Digit( const char& c ) { return '0' <= c && c <= '9'; } int Number( const string& s , const int& n , int& i , vector<vector<int>>& child , int& val_num ) { Debug( s , i , __FUNCTION__ ); int count = 0; while( s[i] == '-' ){ i++; count++; } int i_start = i - ( count % 2 ); while( i < n ? Digit( s[i] ) : false ){ i++; } int i_final = i - 1; // { 左端のi座標 , 右端のi座標 } child.push_back( { i_start , i_final } ); return val_num++; } int Expression( const string& s , const int& n , int& i , vector<vector<int>>& child , int& val_num ); int Factor( const string& s , const int& n , int& i , vector<vector<int>>& child , int& val_num ) { Debug( s , i , __FUNCTION__ ); if( s[i] == '(' ){ i++; int temp = Expression( s , n , i , child , val_num ); i++; return temp; } return Number( s , n , i , child , val_num ); } int Term( const string& s , const int& n , int& i , vector<vector<int>>& child , int& val_num ) { Debug( s , i , __FUNCTION__ ); int l = Factor( s , n , i , child , val_num ); while( i < n ? s[i] == '*' :false ){ int m = i; ++i; int r = Factor( s , n , i , child , val_num ); // { *演算子のi座標 , 引数1のval_num , 引数2のval_num } child.push_back( { m , l , r } ); l = val_num++; } return l; } int Expression( const string& s , const int& n , int& i , vector<vector<int>>& child , int& val_num ) { Debug( s , i , __FUNCTION__ ); int l = Term( s , n , i , child , val_num ); while( i < n ? s[i] == '+' || s[i] == '-' : false ){ int m = i; ++i; int r = Term( s , n , i , child , val_num ); // { +-演算子のi座標 , 引数1のval_num , 引数2のval_num } child.push_back( { m , l , r } ); l = val_num++; } return l; } int SyntaxAnalysis( const string& s , const int& n , vector<vector<int>>& child ) { int val_num = 0; int i = 0; return Expression( s , n , i , child , val_num ); } // いつかライブラリー化したいけど問題ごとに左右とかの決め方を変えなくていいのかの経験知が足りないので // 今回はとりあえず直に書く。セグ木使う問題ではないので再帰順にさえ注意すればよさそう。 void HLDecomposition( vector<vector<int>>& child , const int& root , list<int>& endpoint ) { vector<int> weight( root + 1 ); // 今回はノードのval_num座標を子ノードのval_num座標より大きく取っているので左から再帰。 FOREQ( v , 0 , root ){ if( child[v].size() == leaf_size ){ weight[v] = child[v][1] - child[v][0]; endpoint.push_back( v ); } else { // 左をHeigh、右をLowに固定。 if( weight[child[v][1]] < weight[child[v][2]] ){ swap( child[v][1] , child[v][2] ); } weight[v] = weight[child[v][1]] + weight[child[v][2]]; if( child[child[v][2]].size() != leaf_size ){ endpoint.push_back( child[v][2] ); } } } endpoint.push_back( root ); } // Gary L. Miller and John H. Reif, Parallel Tree Contraction--Part I: Fundamentals., 1989. // を参考。演算のcompress+rakeをO(log N)回処理。 bigint Evaluate( const string& s , const vector<vector<int>>& child , const int& root , const list<int>& endpoint ) { vector<Func> val( root + 1 ); FOR_ITR( endpoint ){ list<Func> temp{}; int v = *itr; int w = v; while( true ){ if( child[w].size() == leaf_size ){ temp.push_back( { 0 , s.substr( child[w][0] , child[w][1] - child[w][0] + 1 ) } ); break; } else { assert( val[child[w][2]].first == 0 ); if( s[child[w][0]] == '+' ){ temp.push_back( { 1 , move( val[child[w][2]].second ) } ); } else if( s[child[w][0]] == '-' ){ if( child[w][1] < child[w][2] ){ val[child[w][2]].second.neg = !val[child[w][2]].second.neg; temp.push_back( { 1 , move( val[child[w][2]].second ) } ); } else { temp.push_back( { -1 , move( val[child[w][2]].second ) } ); } } else if( s[child[w][0]] == '*' ){ temp.push_back( { move( val[child[w][2]].second ) , 0 } ); } else { abort(); } } w = child[w][1]; } val[v] = move( Prod( temp ) ); Debug( s , child , val , v ); } return move( val[root].second ); } inline void Solve() { CIN( int , N ); CIN( string , S ); vector<vector<int>> child{}; int root = SyntaxAnalysis( S , N , child ); abort(); // 06_large_01.txtでTLE assert( root == child.size() - 1 ); list<int> endpoint{}; HLDecomposition( child , root , endpoint ); // abort(); // 06_large_01.txtでTLE RETURN( Evaluate( S , child , root , endpoint ) ); // 05_large_01.txtでTLE } REPEAT_MAIN(1); #else // INCLUDE_MAIN #ifdef INCLUDE_SUB template <typename PATH> list<PATH> E( const int& i ) { // list<PATH> answer{}; list<PATH> answer = e<PATH>[i]; // VVV 入力によらない処理は以下に挿入する。 // AAA 入力によらない処理は以上に挿入する。 return answer; } template <typename T> inline T F( const T& t ){ return f<T>[t]; } template <typename T> inline T G( const int& i ){ return g<T>[i]; } // COMPAREに使用。圧縮時は削除する。 ll Naive( int N , int M , int K ) { ll answer = N + M + K; return answer; } // COMPAREに使用。圧縮時は削除する。 ll Answer( ll N , ll M , ll K ) { // START_WATCH; ll answer = N + M + K; // // TLに準じる乱択や全探索。デフォルトの猶予は100.0[ms]。 // CEXPR( double , TL , 2000.0 ); // while( CHECK_WATCH( TL ) ){ // } return answer; } // 圧縮時は中身だけ削除する。 inline void Experiment() { // CEXPR( int , bound , 10 ); // FOREQ( N , 0 , bound ){ // FOREQ( M , 0 , bound ){ // FOREQ( K , 0 , bound ){ // COUT( N , M , K , ":" , Naive( N , M , K ) ); // } // } // // cout << Naive( N ) << ",\n"[N==bound]; // } } // 圧縮時は中身だけ削除する。 inline void SmallTest() { // CEXPR( int , bound , 10 ); // FOREQ( N , 0 , bound ){ // FOREQ( M , 0 , bound ){ // FOREQ( K , 0 , bound ){ // COMPARE( N , M , K ); // } // } // // COMPARE( N ); // } } #define INCLUDE_MAIN #include __FILE__ #else // INCLUDE_SUB #ifdef INCLUDE_LIBRARY /* C-x 3 C-x o C-x C-fによるファイル操作用 BFS: c:/Users/user/Documents/Programming/Mathematics/Geometry/Graph/BreadthFirstSearch/compress.txt CoordinateCompress: c:/Users/user/Documents/Programming/Mathematics/SetTheory/DirectProduct/CoordinateCompress/compress.txt DFSOnTree c:/Users/user/Documents/Programming/Mathematics/Geometry/Graph/DepthFirstSearch/Tree/a.hpp Divisor: c:/Users/user/Documents/Programming/Mathematics/Arithmetic/Prime/Divisor/compress.txt Polynomial c:/Users/user/Documents/Programming/Mathematics/Polynomial/compress.txt UnionFind c:/Users/user/Documents/Programming/Utility/VLTree/UnionFindForest/compress.txt */ // VVV 常設でないライブラリは以下に挿入する。 // https://yukicoder.me/problems/no/2595 // で指定されたライブラリ // https://github.com/SSRS-cp/yuki2595-bigint-lib // を添付。 #include <vector> #include <string> #include <atcoder/convolution> const int DIGIT = 6; const int BASE = 1000000; struct positive_bigint{ std::vector<int> d; positive_bigint(){ } positive_bigint(long long X){ while (X > 0){ d.push_back(X % BASE); X /= BASE; } } positive_bigint(std::string S){ if (S == "0"){ S = ""; } int L = S.size(); d.resize((L + DIGIT - 1) / DIGIT, 0); for (int i = L - 1; i >= 0; i -= 6){ for (int j = std::max(i - 5, 0); j <= i; j++){ d[i / DIGIT] *= 10; d[i / DIGIT] += S[j] - '0'; } } std::reverse(d.begin(), d.end()); } bool empty() const { return d.empty(); } int size() const { return d.size(); } int& operator [](int i){ return d[i]; } int operator [](int i) const { return d[i]; } }; std::string to_string(const positive_bigint &A){ int N = A.size(); std::string ans; for (int i = N - 1; i >= 0; i--){ std::string tmp = std::to_string(A[i]); if (i < N - 1){ ans += std::string(DIGIT - tmp.size(), '0'); } ans += tmp; } if (ans.empty()){ ans = "0"; } return ans; } std::istream& operator >>(std::istream &is, positive_bigint &A){ std::string S; is >> S; A = positive_bigint(S); return is; } std::ostream& operator <<(std::ostream &os, positive_bigint &A){ os << to_string(A); return os; } int cmp(const positive_bigint &A, const positive_bigint &B){ int N = A.size(); int M = B.size(); if (N < M){ return -1; } else if (N > M){ return 1; } else { for (int i = N - 1; i >= 0; i--){ if (A[i] < B[i]){ return -1; } if (A[i] > B[i]){ return 1; } } return 0; } } bool operator ==(const positive_bigint &A, const positive_bigint &B){ return cmp(A, B) == 0; } bool operator !=(const positive_bigint &A, const positive_bigint &B){ return cmp(A, B) != 0; } bool operator <(const positive_bigint &A, const positive_bigint &B){ return cmp(A, B) < 0; } bool operator >(const positive_bigint &A, const positive_bigint &B){ return cmp(A, B) > 0; } bool operator <=(const positive_bigint &A, const positive_bigint &B){ return cmp(A, B) <= 0; } bool operator >=(const positive_bigint &A, const positive_bigint &B){ return cmp(A, B) >= 0; } positive_bigint& operator +=(positive_bigint &A, const positive_bigint &B){ int N = A.size(); int M = B.size(); while (N < M){ A.d.push_back(0); N++; } for (int i = 0; i < M; i++){ A[i] += B[i]; } for (int i = 0; i < N - 1; i++){ if (A[i] >= BASE){ A[i] -= BASE; A[i + 1]++; } } if (N > 0){ if (A[N - 1] >= BASE){ A.d.push_back(1); A[N - 1] -= BASE; } } return A; } positive_bigint operator +(const positive_bigint &A, const positive_bigint &B){ positive_bigint A2 = A; A2 += B; return A2; } positive_bigint& operator -=(positive_bigint &A, const positive_bigint &B){ int N = A.size(); int M = B.size(); for (int i = 0; i < M; i++){ A[i] -= B[i]; } for (int i = 0; i < N - 1; i++){ if (A[i] < 0){ A[i] += BASE; A[i + 1]--; } } while (!A.empty()){ if (A.d.back() == 0){ A.d.pop_back(); } else { break; } } return A; } positive_bigint operator -(const positive_bigint &A, const positive_bigint &B){ positive_bigint A2 = A; A2 -= B; return A2; } positive_bigint operator *(const positive_bigint &A, const positive_bigint &B){ if (A.empty() || B.empty()){ return 0; } int N = A.size(); int M = B.size(); std::vector<long long> a(N); for (int i= 0; i < N; i++){ a[i] = A[i]; } std::vector<long long> b(M); for (int i = 0; i < M; i++){ b[i] = B[i]; } std::vector<long long> C = atcoder::convolution_ll(a, b); for (int i = 0; i < N + M - 2; i++){ C[i + 1] += C[i] / BASE; C[i] %= BASE; } if (C[N + M - 2] >= BASE){ C.resize(N + M); C[N + M - 1] += C[N + M - 2] / BASE; C[N + M - 2] %= BASE; } positive_bigint ans; ans.d.resize(C.size()); for (int i = 0; i < C.size(); i++){ ans[i] = C[i]; } return ans; } positive_bigint operator *=(positive_bigint &A, const positive_bigint &B){ A = A * B; return A; } struct bigint{ bool neg = false; positive_bigint a; bigint(){ } bigint(long long X): neg(X < 0), a(abs(X)){ } bigint(const positive_bigint &X, bool neg = false): neg(neg), a(X){ } bigint(const std::string &s){ if (!s.empty()){ if (s[0] == '-'){ neg = true; a = positive_bigint(s.substr(1, s.size() - 1)); } else { a = positive_bigint(s); } } } bool empty() const { return a.empty(); } int size() const { return a.size(); } int& operator [](int i){ return a[i]; } }; std::string to_string(const bigint &A){ std::string ans; if (A.neg){ ans += '-'; } ans += to_string(A.a); return ans; } std::istream& operator >>(std::istream &is, bigint &A){ std::string S; is >> S; if (S != "0"){ A = bigint(S); } return is; } std::ostream& operator <<(std::ostream &os, bigint A){ os << to_string(A); return os; } positive_bigint abs(const bigint &A){ return A.a; } int cmp(const bigint &A, const bigint &B){ if (!A.neg){ if (!B.neg){ return cmp(A.a, B.a); } else { return 1; } } else { if (!B.neg){ return -1; } else { return cmp(B.a, A.a); } } } bool operator ==(const bigint &A, const bigint &B){ return cmp(A, B) == 0; } bool operator !=(const bigint &A, const bigint &B){ return cmp(A, B) != 0; } bool operator <(const bigint &A, const bigint &B){ return cmp(A, B) < 0; } bool operator >(const bigint &A, const bigint &B){ return cmp(A, B) > 0; } bool operator <=(const bigint &A, const bigint &B){ return cmp(A, B) <= 0; } bool operator >=(const bigint &A, const bigint &B){ return cmp(A, B) >= 0; } bigint operator +(const bigint &A){ return A; } bigint operator -(const bigint &A){ bigint A2 = A; if (!A2.empty()){ A2.neg = !A2.neg; } return A2; } bigint& operator +=(bigint &A, const bigint &B){ if (A.neg == B.neg){ A.a += B.a; } else { int c = cmp(A.a, B.a); if (c > 0){ A.a -= B.a; } else if (c < 0){ A.a = B.a - A.a; A.neg = !A.neg; } else { A = 0; } } return A; } bigint operator +(const bigint &A, const bigint &B){ bigint A2 = A; A2 += B; return A2; } bigint& operator -=(bigint &A, const bigint &B){ if (A.neg != B.neg){ A.a += B.a; } else { int c = cmp(A.a, B.a); if (c > 0){ A.a -= B.a; } else if (c < 0){ A.a = B.a - A.a; A.neg = !A.neg; } else { A = 0; } } return A; } bigint operator -(const bigint &A, const bigint &B){ bigint A2 = A; A2 -= B; return A2; } bigint operator *=(bigint &A, const bigint &B){ if (A.empty() || B.empty()){ A = 0; } else { if (B.neg){ A.neg = !A.neg; } A.a *= B.a; } return A; } bigint operator *(const bigint &A, const bigint &B){ bigint A2 = A; A2 *= B; return A2; } // AAA 常設でないライブラリは以上に挿入する。 #define INCLUDE_SUB #include __FILE__ #else // INCLUDE_LIBRARY // #define REACTIVE // #define USE_GETLINE #ifdef DEBUG #define _GLIBCXX_DEBUG #define REPEAT_MAIN( BOUND ) START_MAIN; signal( SIGABRT , &AlertAbort ); AutoCheck( exec_mode , use_getline ); if( exec_mode == sample_debug_mode || exec_mode == submission_debug_mode || exec_mode == library_search_mode ){ return 0; } else if( exec_mode == experiment_mode ){ Experiment(); return 0; } else if( exec_mode == small_test_mode ){ SmallTest(); return 0; }; DEXPR( int , bound_test_case_num , BOUND , min( BOUND , 100 ) ); int test_case_num = 1; if( exec_mode == solve_mode ){ if constexpr( bound_test_case_num > 1 ){ SET_ASSERT( test_case_num , 1 , bound_test_case_num ); } } else if( exec_mode == random_test_mode ){ CERR( "ランダムテストを行う回数を指定してください。" ); SET_LL( test_case_num ); } FINISH_MAIN #define DEXPR( LL , BOUND , VALUE , DEBUG_VALUE ) CEXPR( LL , BOUND , DEBUG_VALUE ) #define ASSERT( A , MIN , MAX ) CERR( "ASSERTチェック: " , ( MIN ) , ( ( MIN ) <= A ? "<=" : ">" ) , A , ( A <= ( MAX ) ? "<=" : ">" ) , ( MAX ) ); assert( ( MIN ) <= A && A <= ( MAX ) ) #define SET_ASSERT( A , MIN , MAX ) if( exec_mode == solve_mode ){ SET_LL( A ); ASSERT( A , MIN , MAX ); } else if( exec_mode == random_test_mode ){ CERR( #A , " = " , ( A = GetRand( MIN , MAX ) ) ); } else { assert( false ); } #define SOLVE_ONLY static_assert( __FUNCTION__[0] == 'S' ) #define CERR( ... ) VariadicCout( cerr , __VA_ARGS__ ) << endl #define COUT( ... ) VariadicCout( cout << "出力: " , __VA_ARGS__ ) << endl #define CERR_A( A , N ) OUTPUT_ARRAY( cerr , A , N ) << endl #define COUT_A( A , N ) cout << "出力: "; OUTPUT_ARRAY( cout , A , N ) << endl #define CERR_ITR( A ) OUTPUT_ITR( cerr , A ) << endl #define COUT_ITR( A ) cout << "出力: "; OUTPUT_ITR( cout , A ) << endl #else #pragma GCC optimize ( "O3" ) #pragma GCC optimize ( "unroll-loops" ) #pragma GCC target ( "sse4.2,fma,avx2,popcnt,lzcnt,bmi2" ) #define REPEAT_MAIN( BOUND ) START_MAIN; CEXPR( int , bound_test_case_num , BOUND ); int test_case_num = 1; if constexpr( bound_test_case_num > 1 ){ SET_ASSERT( test_case_num , 1 , bound_test_case_num ); } FINISH_MAIN #define DEXPR( LL , BOUND , VALUE , DEBUG_VALUE ) CEXPR( LL , BOUND , VALUE ) #define ASSERT( A , MIN , MAX ) assert( ( MIN ) <= A && A <= ( MAX ) ) #define SET_ASSERT( A , MIN , MAX ) SET_LL( A ); ASSERT( A , MIN , MAX ) #define SOLVE_ONLY #define CERR( ... ) #define COUT( ... ) VariadicCout( cout , __VA_ARGS__ ) << ENDL #define CERR_A( A , N ) #define COUT_A( A , N ) OUTPUT_ARRAY( cout , A , N ) << ENDL #define CERR_ITR( A ) #define COUT_ITR( A ) OUTPUT_ITR( cout , A ) << ENDL #endif #ifdef REACTIVE #define ENDL endl #else #define ENDL "\n" #endif #ifdef USE_GETLINE #define SET_LL( A ) { GETLINE( A ## _str ); A = stoll( A ## _str ); } #define GETLINE_SEPARATE( SEPARATOR , ... ) SOLVE_ONLY; string __VA_ARGS__; VariadicGetline( cin , SEPARATOR , __VA_ARGS__ ) #define GETLINE( ... ) SOLVE_ONLY; GETLINE_SEPARATE( '\n' , __VA_ARGS__ ) #else #define SET_LL( A ) cin >> A #define CIN( LL , ... ) SOLVE_ONLY; LL __VA_ARGS__; VariadicCin( cin , __VA_ARGS__ ) #define SET_A( A , N ) SOLVE_ONLY; FOR( VARIABLE_FOR_CIN_A , 0 , N ){ cin >> A[VARIABLE_FOR_CIN_A]; } #define CIN_A( LL , A , N ) vector<LL> A( N ); SET_A( A , N ); #endif #include <bits/stdc++.h> using namespace std; using uint = unsigned int; using ll = long long; using ull = unsigned long long; using ld = long double; using lld = __float128; template <typename INT> using T2 = pair<INT,INT>; template <typename INT> using T3 = tuple<INT,INT,INT>; template <typename INT> using T4 = tuple<INT,INT,INT,INT>; using path = pair<int,ll>; #define ATT __attribute__( ( target( "sse4.2,fma,avx2,popcnt,lzcnt,bmi2" ) ) ) #define START_MAIN int main(){ ios_base::sync_with_stdio( false ); cin.tie( nullptr ) #define FINISH_MAIN REPEAT( test_case_num ){ if constexpr( bound_test_case_num > 1 ){ CERR( "testcase " , VARIABLE_FOR_REPEAT_test_case_num , ":" ); } Solve(); CERR( "" ); } } #define START_WATCH chrono::system_clock::time_point watch = chrono::system_clock::now() #define CURRENT_TIME static_cast<double>( chrono::duration_cast<chrono::microseconds>( chrono::system_clock::now() - watch ).count() / 1000.0 ) #define CHECK_WATCH( TL_MS ) ( CURRENT_TIME < TL_MS - 100.0 ) #define TYPE_OF( VAR ) decay_t<decltype( VAR )> #define CEXPR( LL , BOUND , VALUE ) constexpr LL BOUND = VALUE #define CIN_ASSERT( A , MIN , MAX ) TYPE_OF( MAX ) A; SET_ASSERT( A , MIN , MAX ) #define FOR( VAR , INITIAL , FINAL_PLUS_ONE ) for( TYPE_OF( FINAL_PLUS_ONE ) VAR = INITIAL ; VAR < FINAL_PLUS_ONE ; VAR ++ ) #define FOREQ( VAR , INITIAL , FINAL ) for( TYPE_OF( FINAL ) VAR = INITIAL ; VAR <= FINAL ; VAR ++ ) #define FOREQINV( VAR , INITIAL , FINAL ) for( TYPE_OF( INITIAL ) VAR = INITIAL ; VAR + 1 > FINAL ; VAR -- ) #define AUTO_ITR( ARRAY ) auto itr_ ## ARRAY = ARRAY .begin() , end_ ## ARRAY = ARRAY .end() #define FOR_ITR( ARRAY ) for( AUTO_ITR( ARRAY ) , itr = itr_ ## ARRAY ; itr_ ## ARRAY != end_ ## ARRAY ; itr_ ## ARRAY ++ , itr++ ) #define REPEAT( HOW_MANY_TIMES ) FOR( VARIABLE_FOR_REPEAT_ ## HOW_MANY_TIMES , 0 , HOW_MANY_TIMES ) #define SET_PRECISION( DECIMAL_DIGITS ) cout << fixed << setprecision( DECIMAL_DIGITS ) #define OUTPUT_ARRAY( OS , A , N ) FOR( VARIABLE_FOR_OUTPUT_ARRAY , 0 , N ){ OS << A[VARIABLE_FOR_OUTPUT_ARRAY] << (VARIABLE_FOR_OUTPUT_ARRAY==N-1?"":" "); } OS #define OUTPUT_ITR( OS , A ) { auto ITERATOR_FOR_OUTPUT_ITR = A.begin() , END_FOR_OUTPUT_ITR = A.end(); bool VARIABLE_FOR_OUTPUT_ITR = ITERATOR_FOR_COUT_ITR != END_FOR_COUT_ITR; while( VARIABLE_FOR_OUTPUT_ITR ){ OS << *ITERATOR_FOR_COUT_ITR; ( VARIABLE_FOR_OUTPUT_ITR = ++ITERATOR_FOR_COUT_ITR != END_FOR_COUT_ITR ) ? OS : OS << " "; } } OS #define RETURN( ... ) SOLVE_ONLY; COUT( __VA_ARGS__ ); return #define COMPARE( ... ) auto naive = Naive( __VA_ARGS__ ); auto answer = Answer( __VA_ARGS__ ); bool match = naive == answer; COUT( "(" , #__VA_ARGS__ , ") == (" , __VA_ARGS__ , ") : Naive == " , naive , match ? "==" : "!=" , answer , "== Answer" ); if( !match ){ return; } // 入出力用 template <class Traits> inline basic_istream<char,Traits>& VariadicCin( basic_istream<char,Traits>& is ) { return is; } template <class Traits , typename Arg , typename... ARGS> inline basic_istream<char,Traits>& VariadicCin( basic_istream<char,Traits>& is , Arg& arg , ARGS&... args ) { return VariadicCin( is >> arg , args... ); } template <class Traits> inline basic_istream<char,Traits>& VariadicGetline( basic_istream<char,Traits>& is , const char& separator ) { return is; } template <class Traits , typename Arg , typename... ARGS> inline basic_istream<char,Traits>& VariadicGetline( basic_istream<char,Traits>& is , const char& separator , Arg& arg , ARGS&... args ) { return VariadicGetline( getline( is , arg , separator ) , separator , args... ); } template <class Traits , typename Arg> inline basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os , const vector<Arg>& arg ) { auto begin = arg.begin() , end = arg.end(); auto itr = begin; while( itr != end ){ ( itr == begin ? os : os << " " ) << *itr; itr++; } return os; } template <class Traits , typename Arg> inline basic_ostream<char,Traits>& VariadicCout( basic_ostream<char,Traits>& os , const Arg& arg ) { return os << arg; } template <class Traits , typename Arg1 , typename Arg2 , typename... ARGS> inline basic_ostream<char,Traits>& VariadicCout( basic_ostream<char,Traits>& os , const Arg1& arg1 , const Arg2& arg2 , const ARGS&... args ) { return VariadicCout( os << arg1 << " " , arg2 , args... ); } // 算術用 template <typename T> constexpr T PositiveBaseResidue( const T& a , const T& p ){ return a >= 0 ? a % p : p - 1 - ( ( - ( a + 1 ) ) % p ); } template <typename T> constexpr T Residue( const T& a , const T& p ){ return PositiveBaseResidue( a , p < 0 ? -p : p ); } template <typename T> constexpr T PositiveBaseQuotient( const T& a , const T& p ){ return ( a - PositiveBaseResidue( a , p ) ) / p; } template <typename T> constexpr T Quotient( const T& a , const T& p ){ return p < 0 ? PositiveBaseQuotient( -a , -p ) : PositiveBaseQuotient( a , p ); } #define POWER( ANSWER , ARGUMENT , EXPONENT ) \ static_assert( ! is_same<TYPE_OF( ARGUMENT ),int>::value && ! is_same<TYPE_OF( ARGUMENT ),uint>::value ); \ TYPE_OF( ARGUMENT ) ANSWER{ 1 }; \ { \ TYPE_OF( ARGUMENT ) ARGUMENT_FOR_SQUARE_FOR_POWER = ( ARGUMENT ); \ TYPE_OF( EXPONENT ) EXPONENT_FOR_SQUARE_FOR_POWER = ( EXPONENT ); \ while( EXPONENT_FOR_SQUARE_FOR_POWER != 0 ){ \ if( EXPONENT_FOR_SQUARE_FOR_POWER % 2 == 1 ){ \ ANSWER *= ARGUMENT_FOR_SQUARE_FOR_POWER; \ } \ ARGUMENT_FOR_SQUARE_FOR_POWER *= ARGUMENT_FOR_SQUARE_FOR_POWER; \ EXPONENT_FOR_SQUARE_FOR_POWER /= 2; \ } \ } \ #define POWER_MOD( ANSWER , ARGUMENT , EXPONENT , MODULO ) \ ll ANSWER{ 1 }; \ { \ ll ARGUMENT_FOR_SQUARE_FOR_POWER = ( ( ARGUMENT ) % ( MODULO ) ) % ( MODULO ); \ ARGUMENT_FOR_SQUARE_FOR_POWER < 0 ? ARGUMENT_FOR_SQUARE_FOR_POWER += ( MODULO ) : ARGUMENT_FOR_SQUARE_FOR_POWER; \ TYPE_OF( EXPONENT ) EXPONENT_FOR_SQUARE_FOR_POWER = ( EXPONENT ); \ while( EXPONENT_FOR_SQUARE_FOR_POWER != 0 ){ \ if( EXPONENT_FOR_SQUARE_FOR_POWER % 2 == 1 ){ \ ANSWER = ( ANSWER * ARGUMENT_FOR_SQUARE_FOR_POWER ) % ( MODULO ); \ } \ ARGUMENT_FOR_SQUARE_FOR_POWER = ( ARGUMENT_FOR_SQUARE_FOR_POWER * ARGUMENT_FOR_SQUARE_FOR_POWER ) % ( MODULO ); \ EXPONENT_FOR_SQUARE_FOR_POWER /= 2; \ } \ } \ #define FACTORIAL_MOD( ANSWER , ANSWER_INV , INVERSE , MAX_INDEX , CONSTEXPR_LENGTH , MODULO ) \ ll ANSWER[CONSTEXPR_LENGTH]; \ ll ANSWER_INV[CONSTEXPR_LENGTH]; \ ll INVERSE[CONSTEXPR_LENGTH]; \ { \ ll VARIABLE_FOR_PRODUCT_FOR_FACTORIAL = 1; \ ANSWER[0] = VARIABLE_FOR_PRODUCT_FOR_FACTORIAL; \ FOREQ( i , 1 , MAX_INDEX ){ \ ANSWER[i] = ( VARIABLE_FOR_PRODUCT_FOR_FACTORIAL *= i ) %= ( MODULO ); \ } \ ANSWER_INV[0] = ANSWER_INV[1] = INVERSE[1] = VARIABLE_FOR_PRODUCT_FOR_FACTORIAL = 1; \ FOREQ( i , 2 , MAX_INDEX ){ \ ANSWER_INV[i] = ( VARIABLE_FOR_PRODUCT_FOR_FACTORIAL *= INVERSE[i] = ( MODULO ) - ( ( ( ( MODULO ) / i ) * INVERSE[ ( MODULO ) % i ] ) % ( MODULO ) ) ) %= ( MODULO ); \ } \ } \ // 二分探索用 // EXPRESSIONがANSWERの広義単調関数の時、EXPRESSION >= CONST_TARGETの整数解を格納。 #define BS( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , DESIRED_INEQUALITY , CONST_TARGET , INEQUALITY_FOR_CHECK , UPDATE_U , UPDATE_L , UPDATE_ANSWER ) \ static_assert( ! is_same<TYPE_OF( CONST_TARGET ),uint>::value && ! is_same<TYPE_OF( CONST_TARGET ),ull>::value ); \ ll ANSWER = MINIMUM; \ { \ ll L_BS = MINIMUM; \ ll U_BS = MAXIMUM; \ ANSWER = UPDATE_ANSWER; \ ll EXPRESSION_BS; \ const ll CONST_TARGET_BS = ( CONST_TARGET ); \ ll DIFFERENCE_BS; \ while( L_BS < U_BS ){ \ DIFFERENCE_BS = ( EXPRESSION_BS = ( EXPRESSION ) ) - CONST_TARGET_BS; \ CERR( "二分探索中:" , "L_BS =" , L_BS , "<=" , ANSWER , "<=" , U_BS , "= U_BS :" , #EXPRESSION , "-" , #CONST_TARGET , "=" , EXPRESSION_BS , "-" , CONST_TARGET_BS , "=" , DIFFERENCE_BS ); \ if( DIFFERENCE_BS INEQUALITY_FOR_CHECK 0 ){ \ U_BS = UPDATE_U; \ } else { \ L_BS = UPDATE_L; \ } \ ANSWER = UPDATE_ANSWER; \ } \ if( L_BS > U_BS ){ \ CERR( "二分探索失敗:" , "L_BS =" , L_BS , ">" , U_BS , "= U_BS :" , #ANSWER , ":=" , #MAXIMUM , "+ 1 =" , MAXIMUM + 1 ); \ CERR( "二分探索マクロにミスがある可能性があります。変更前の版に戻してください。" ); \ ANSWER = MAXIMUM + 1; \ } else { \ CERR( "二分探索終了:" , "L_BS =" , L_BS , "<=" , ANSWER , "<=" , U_BS , "= U_BS" ); \ CERR( "二分探索が成功したかを確認するために" , #EXPRESSION , "を計算します。" ); \ CERR( "成功判定が不要な場合はこの計算を削除しても構いません。" ); \ EXPRESSION_BS = ( EXPRESSION ); \ CERR( "二分探索結果:" , #EXPRESSION , "=" , EXPRESSION_BS , ( EXPRESSION_BS > CONST_TARGET_BS ? ">" : EXPRESSION_BS < CONST_TARGET_BS ? "<" : "=" ) , CONST_TARGET_BS ); \ if( EXPRESSION_BS DESIRED_INEQUALITY CONST_TARGET_BS ){ \ CERR( "二分探索成功:" , #ANSWER , ":=" , ANSWER ); \ } else { \ CERR( "二分探索失敗:" , #ANSWER , ":=" , #MAXIMUM , "+ 1 =" , MAXIMUM + 1 ); \ CERR( "単調でないか、単調増加性と単調減少性を逆にしてしまったか、探索範囲内に解が存在しません。" ); \ ANSWER = MAXIMUM + 1; \ } \ } \ } \ // 単調増加の時にEXPRESSION >= CONST_TARGETの最小解を格納。 #define BS1( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , CONST_TARGET ) \ BS( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , >= , CONST_TARGET , >= , ANSWER , ANSWER + 1 , ( L_BS + U_BS ) / 2 ) \ // 単調増加の時にEXPRESSION <= CONST_TARGETの最大解を格納。 #define BS2( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , CONST_TARGET ) \ BS( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , <= , CONST_TARGET , > , ANSWER - 1 , ANSWER , ( L_BS + 1 + U_BS ) / 2 ) \ // 単調減少の時にEXPRESSION >= CONST_TARGETの最大解を格納。 #define BS3( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , CONST_TARGET ) \ BS( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , >= , CONST_TARGET , < , ANSWER - 1 , ANSWER , ( L_BS + 1 + U_BS ) / 2 ) \ // 単調減少の時にEXPRESSION <= CONST_TARGETの最小解を格納。 #define BS4( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , CONST_TARGET ) \ BS( ANSWER , MINIMUM , MAXIMUM , EXPRESSION , <= , CONST_TARGET , <= , ANSWER , ANSWER + 1 , ( L_BS + U_BS ) / 2 ) \ // t以下の値が存在すればその最大値のiterator、存在しなければend()を返す。 template <typename T> inline typename set<T>::iterator MaximumLeq( set<T>& S , const T& t ) { const auto end = S.end(); if( S.empty() ){ return end; } auto itr = S.upper_bound( t ); return itr == end ? S.find( *( S.rbegin() ) ) : itr == S.begin() ? end : --itr; } // t未満の値が存在すればその最大値のiterator、存在しなければend()を返す。 template <typename T> inline typename set<T>::iterator MaximumLt( set<T>& S , const T& t ) { const auto end = S.end(); if( S.empty() ){ return end; } auto itr = S.lower_bound( t ); return itr == end ? S.find( *( S.rbegin() ) ) : itr == S.begin() ? end : --itr; } // t以上の値が存在すればその最小値のiterator、存在しなければend()を返す。 template <typename T> inline typename set<T>::iterator MinimumGeq( set<T>& S , const T& t ) { return S.lower_bound( t ); } // tより大きい値が存在すればその最小値のiterator、存在しなければend()を返す。 template <typename T> inline typename set<T>::iterator MinimumGt( set<T>& S , const T& t ) { return S.upper_bound( t ); } // データ構造用 template <typename T> inline T Add( const T& t0 , const T& t1 ) { return t0 + t1; } template <typename T> inline T XorAdd( const T& t0 , const T& t1 ){ return t0 ^ t1; } template <typename T> inline T Multiply( const T& t0 , const T& t1 ) { return t0 * t1; } template <typename T> inline const T& Zero() { static const T z = 0; return z; } template <typename T> inline const T& One() { static const T o = 1; return o; }\ template <typename T> inline T AddInv( const T& t ) { return -t; } template <typename T> inline T Id( const T& v ) { return v; } template <typename T> inline T Min( const T& a , const T& b ){ return a < b ? a : b; } template <typename T> inline T Max( const T& a , const T& b ){ return a < b ? b : a; } // グリッド問題用 int H , W , H_minus , W_minus , HW; vector<vector<bool> > non_wall; inline T2<int> EnumHW( const int& v ) { return { v / W , v % W }; } inline int EnumHW_inv( const int& h , const int& w ) { return h * W + w; } const string direction[4] = {"U","R","D","L"}; // (i,j)->(k,h)の方向番号を取得 inline int DirectionNumberOnGrid( const int& i , const int& j , const int& k , const int& h ){return i<k?2:i>k?0:j<h?1:j>h?3:(assert(false),-1);} // v->wの方向番号を取得 inline int DirectionNumberOnGrid( const int& v , const int& w ){auto [i,j]=EnumHW(v);auto [k,h]=EnumHW(w);return DirectionNumberOnGrid(i,j,k,h);} // 方向番号の反転U<->D、R<->L inline int ReverseDirectionNumberOnGrid( const int& n ){assert(0<=n&&n<4);return(n+2)%4;} inline void SetEdgeOnGrid( const string& Si , const int& i , list<int> ( &e )[] , const char& walkable = '.' ){FOR(j,0,W){if(Si[j]==walkable){int v = EnumHW_inv(i,j);if(i>0){e[EnumHW_inv(i-1,j)].push_back(v);}if(i+1<H){e[EnumHW_inv(i+1,j)].push_back(v);}if(j>0){e[EnumHW_inv(i,j-1)].push_back(v);}if(j+1<W){e[EnumHW_inv(i,j+1)].push_back(v);}}}} inline void SetEdgeOnGrid( const string& Si , const int& i , list<path> ( &e )[] , const char& walkable = '.' ){FOR(j,0,W){if(Si[j]==walkable){const int v=EnumHW_inv(i,j);if(i>0){e[EnumHW_inv(i-1,j)].push_back({v,1});}if(i+1<H){e[EnumHW_inv(i+1,j)].push_back({v,1});}if(j>0){e[EnumHW_inv(i,j-1)].push_back({v,1});}if(j+1<W){e[EnumHW_inv(i,j+1)].push_back({v,1});}}}} inline void SetWallOnGrid( const string& Si , const int& i , vector<vector<bool> >& non_wall , const char& walkable = '.' , const char& unwalkable = '#' ){non_wall.push_back(vector<bool>(W));auto& non_wall_i=non_wall[i];FOR(j,0,W){non_wall_i[j]=Si[j]==walkable?true:(assert(Si[j]==unwalkable),false);}} // グラフ用 template <typename PATH> vector<list<PATH> > e; template <typename T> map<T,T> f; template <typename T> vector<T> g; // デバッグ用 #ifdef DEBUG inline void AlertAbort( int n ) { CERR( "abort関数が呼ばれました。assertマクロのメッセージが出力されていない場合はオーバーフローの有無を確認をしてください。" ); } void AutoCheck( int& exec_mode , const bool& use_getline ); inline void Solve(); inline void Experiment(); inline void SmallTest(); inline void RandomTest(); ll GetRand( const ll& Rand_min , const ll& Rand_max ); int exec_mode; CEXPR( int , solve_mode , 0 ); CEXPR( int , sample_debug_mode , 1 ); CEXPR( int , submission_debug_mode , 2 ); CEXPR( int , library_search_mode , 3 ); CEXPR( int , experiment_mode , 4 ); CEXPR( int , small_test_mode , 5 ); CEXPR( int , random_test_mode , 6 ); #ifdef USE_GETLINE CEXPR( bool , use_getline , true ); #else CEXPR( bool , use_getline , false ); #endif #else ll GetRand( const ll& Rand_min , const ll& Rand_max ) { ll answer = time( NULL ); return answer * rand() % ( Rand_max + 1 - Rand_min ) + Rand_min; } #endif // 圧縮用 #define TE template #define TY typename #define US using #define ST static #define IN inline #define CL class #define PU public #define OP operator #define CE constexpr #define CO const #define NE noexcept #define RE return #define WH while #define VO void #define VE vector #define LI list #define BE begin #define EN end #define SZ size #define MO move #define TH this #define CRI CO int& #define CRUI CO uint& #define CRL CO ll& // VVV 常設ライブラリは以下に挿入する。 // AAA 常設ライブラリは以上に挿入する。 #define INCLUDE_LIBRARY #include __FILE__ #endif // INCLUDE_LIBRARY #endif // INCLUDE_SUB #endif // INCLUDE_MAIN