// #define _GLIBCXX_DEBUG // #include "Utility/Header.hpp" // #include "Utility/String/a_Body.hpp" // #include "Contest/Header.hpp" // #include // using namespace std; // using ll = long long; // using uint = unsigned int; // #include "Mathematics/Arithmetic/Prime/a_Body.hpp" #include using namespace std; using uint = unsigned int; #define UNTIE ios_base::sync_with_stdio( false ); cin.tie( nullptr ) #define CIN( LL , A ) LL A; cin >> A #define TYPE_OF( VAR ) remove_const::type >::type #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 REPEAT( HOW_MANY_TIMES ) FOR( VARIABLE_FOR_REPEAT , 0 , HOW_MANY_TIMES ) #define QUIT return 0 #define RETURN( ANSWER ) cout << ( ANSWER ) << "\n"; QUIT template class TruncatedPolynomial; template class Polynomial { friend class TruncatedPolynomial; protected: vector m_f; // m_fのsize uint m_size; bool m_no_redundant_zero; public: inline Polynomial(); inline Polynomial( const T& t ); inline Polynomial( const Polynomial& f ); inline Polynomial( const uint& i , const T& t ); inline Polynomial( vector&& f ); Polynomial& operator=( const T& t ); Polynomial& operator=( const Polynomial& f ); inline const T& operator[]( const uint& i ) const; inline T& operator[]( const uint& i ); inline Polynomial& operator+=( const T& t ); Polynomial& operator+=( const Polynomial& f ); inline Polynomial& operator-=( const T& t ); Polynomial& operator-=( const Polynomial& f ); Polynomial& operator*=( const T& t ); Polynomial& operator*=( const Polynomial& f ); Polynomial& operator/=( const T& t ); Polynomial& operator%=( const T& t ); inline Polynomial operator-() const; inline const vector& GetCoefficient() const noexcept; inline const uint& size() const noexcept; void RemoveRedundantZero(); inline string Display() const noexcept; static inline const Polynomial& zero(); static inline const T& const_zero(); static inline const T& const_one(); static inline const T& const_minus_one(); }; template bool operator==( const Polynomial& f0 , const T& t1 ); template bool operator==( const Polynomial& f0 , const Polynomial& f1 ); template inline bool operator!=( const Polynomial& f0 , const P& f1 ); template inline Polynomial operator+( const Polynomial& f0 , const P& f1 ); template inline Polynomial operator-( const Polynomial& f ); template inline Polynomial operator-( const Polynomial& f0 , const P& f1 ); template inline Polynomial operator*( const Polynomial& f0 , const P& f1 ); template inline Polynomial operator/( const Polynomial& f0 , const T& t1 ); template inline Polynomial operator%( const Polynomial& f0 , const T& t1 ); template inline Polynomial::Polynomial() : m_f() , m_size( 0 ) , m_no_redundant_zero( true ) {} template inline Polynomial::Polynomial( const T& t ) : Polynomial() { if( t != const_zero() ){ operator[]( 0 ) = t; } } template inline Polynomial::Polynomial( const Polynomial& f ) : m_f( f.m_f ) , m_size( f.m_size ) , m_no_redundant_zero( f.m_no_redundant_zero ) {} template inline Polynomial::Polynomial( const uint& i , const T& t ) : Polynomial() { if( t != const_zero() ){ operator[]( i ) = t; } } template inline Polynomial::Polynomial( vector&& f ) : m_f( move( f ) ) , m_size( m_f.size() ) , m_no_redundant_zero( false ) {} template inline Polynomial& Polynomial::operator=( const T& t ) { m_f.clear(); m_size = 0; operator[]( 0 ) = t; return *this; } template inline Polynomial& Polynomial::operator=( const Polynomial& f ) { m_f = f.m_f; m_size = f.m_size; m_no_redundant_zero = f.m_no_redundant_zero; return *this; } template const T& Polynomial::operator[]( const uint& i ) const { if( m_size <= i ){ return const_zero(); } return m_f[i]; } template inline T& Polynomial::operator[]( const uint& i ) { m_no_redundant_zero = false; if( m_size <= i ){ const T& z = const_zero(); while( m_size <= i ){ m_f.push_back( z ); m_size++; } } return m_f[i]; } template inline Polynomial& Polynomial::operator+=( const T& t ) { operator[]( 0 ) += t; return *this; } template Polynomial& Polynomial::operator+=( const Polynomial& f ) { for( uint i = 0 ; i < f.m_size ; i++ ){ operator[]( i ) += f.m_f[i]; } return *this; } template inline Polynomial& Polynomial::operator-=( const T& t ) { operator[]( 0 ) -= t; return *this; } template Polynomial& Polynomial::operator-=( const Polynomial& f ) { for( uint i = 0 ; i < f.m_size ; i++ ){ operator[]( i ) -= f.m_f[i]; } return *this; } template Polynomial& Polynomial::operator*=( const T& t ) { // Tが位数の小さい環である場合も扱う時は冗長な0が生じやすいので次のコメントアウトを解除する。 // if( ! m_no_redundant_zero ){ // RemoveRedundantZero(); // } if( m_size == 0 || t == const_one() ){ return *this; } if( t == const_zero() ){ return operator=( zero() ); } for( uint i = 0 ; i < m_size ; i++ ){ operator[]( i ) *= t; } // Tが整域でない場合も扱う時は冗長な0が生じうるので次のコメントアウトを解除する。 // RemoveRedundantZero(); return *this; } template Polynomial& Polynomial::operator*=( const Polynomial& f ) { // Tが位数の小さい環である場合も扱う時は冗長な0が生じやすいので次のコメントアウトを解除する。 // if( ! m_no_redundant_zero ){ // RemoveRedundantZero(); // } if( m_size == 0 ){ return *this; } if( f.m_size == 0 ){ return operator=( zero() ); } const uint size = m_size + f.m_size - 1; Polynomial product{}; for( uint i = 0 ; i < size ; i++ ){ T& product_i = product[i]; const uint j_min = m_size <= i ? i - m_size + 1 : 0; const uint j_lim = i < f.m_size ? i + 1 : f.m_size; for( uint j = j_min ; j < j_lim ; j++ ){ product_i += m_f[i - j] * f.m_f[j]; } } // Tが整域でない場合も扱う時は冗長な0が生じうるので次のコメントアウトを解除する。 // product.RemoveRedundantZero(); return operator=( product ); } template Polynomial& Polynomial::operator/=( const T& t ) { // Tが位数の小さい環である場合も扱う時は冗長な0が生じやすいので次のコメントアウトを解除する。 // if( ! m_no_redundant_zero ){ // RemoveRedundantZero(); // } if( t == const_one() ){ return *this; } for( uint i = 0 ; i < m_size ; i++ ){ operator[]( i ) /= t; } // Tが体でない場合も扱う時は冗長な0が生じうるので次のコメントアウトを解除する。 // RemoveRedundantZero(); return *this; } template Polynomial& Polynomial::operator%=( const T& t ) { // Tが位数の小さい環である場合も扱う時は冗長な0が生じやすいので次のコメントアウトを解除する。 // if( ! m_no_redundant_zero ){ // RemoveRedundantZero(); // } if( t == const_one() ){ return operator=( zero() ); } for( uint i = 0 ; i < m_size ; i++ ){ operator[]( i ) %= t; } // Tが位数の小さい環である場合も扱う時は冗長な0が生じやすいので次のコメントアウトを解除する。 // RemoveRedundantZero(); return *this; } template inline Polynomial Polynomial::operator-() const { Polynomial().operator-=( *this ); } template inline const vector& Polynomial::GetCoefficient() const noexcept { return m_f; } template inline const uint& Polynomial::size() const noexcept { return m_size; } template void Polynomial::RemoveRedundantZero() { if( m_no_redundant_zero ){ return; } const T& z = const_zero(); while( m_size > 0 ? m_f[m_size - 1] == z : false ){ m_f.pop_back(); m_size--; } m_no_redundant_zero = true; return; } template string Polynomial::Display() const noexcept { string s = "("; if( m_size > 0 ){ s += to_string( m_f[0] ); for( uint i = 1 ; i < m_size ; i++ ){ s += ", " + to_string( m_f[i] ); } } s += ")"; return s; } template inline const Polynomial& Polynomial::zero() { static const Polynomial z{}; return z; } template inline const T& Polynomial::const_zero() { static const T z{ 0 }; return z; } template inline const T& Polynomial::const_one() { static const T o{ 1 }; return o; } template inline const T& Polynomial::const_minus_one() { static const T m{ -1 }; return m; } template bool operator==( const Polynomial& f0 , const T& t1 ) { const uint& size = f0.size(); const T& zero = Polynomial::const_zero(); for( uint i = 1 ; i < size ; i++ ){ if( f0[i] != zero ){ return false; } } return f0[0] == t1; } template bool operator==( const Polynomial& f0 , const Polynomial& f1 ) { const uint& size0 = f0.size(); const uint& size1 = f1.size(); const uint& size = size0 < size1 ? size1 : size0; for( uint i = 0 ; i < size ; i++ ){ if( f0[i] != f1[i] ){ return false; } } return true; } template inline bool operator!=( const Polynomial& f0 , const P& f1 ) { return !( f0 == f1 ); } template inline Polynomial operator+( const Polynomial& f0 , const P& f1 ) { Polynomial f = f0; f += f1; return f; } template inline Polynomial operator-( const Polynomial& f ) { return Polynomial::zero() - f; } template inline Polynomial operator-( const Polynomial& f0 , const P& f1 ) { Polynomial f = f0; return f.operator-=( f1 ); } template inline Polynomial operator*( const Polynomial& f0 , const P& f1 ) { Polynomial f = f0; return f.operator*=( f1 ); } template inline Polynomial operator/( const Polynomial& f0 , const T& t1 ) { Polynomial f = f0; return f.operator/=( t1 ); } template inline Polynomial operator%( const Polynomial& f0 , const T& t1 ) { Polynomial f = f0; return f.operator%=( t1 ); } #define RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( CONDITION ) \ if( CONDITION ){ \ \ return operator=( zero ); \ \ } \ \ #define RETURN_ZERO_FOR_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL_IF( CONDITION ) \ if( CONDITION ){ \ \ return TruncatedPolynomial( m_N ); \ \ } \ \ #define SET_VECTOR_FOR_ANSWER_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( N_OUTPUT_LIM ) \ if( Polynomial::m_size < N_OUTPUT_LIM ){ \ \ for( uint i = Polynomial::m_size ; i < N_OUTPUT_LIM ; i++ ){ \ \ Polynomial::m_f.push_back( 0 ); \ \ } \ \ Polynomial::m_size = N_OUTPUT_LIM; \ \ } \ #define SET_VECTOR_FOR_ANSWER_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL( N_OUTPUT_LIM ) \ vector answer( N_OUTPUT_LIM ) \ #define SET_SUM_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ Polynomial::m_f[i] = sum \ #define SET_SUM_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL \ answer[i] = sum \ #define SET_N_INPUT_START_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( F , SIZE , N_INPUT_START_NUM ) \ uint N_INPUT_START_NUM; \ \ for( uint i = 0 ; i < SIZE && searching ; i++ ){ \ \ if( F[i] != zero ){ \ \ N_INPUT_START_NUM = i; \ searching = false; \ \ } \ \ } \ \ #define SET_N_INPUT_MAX_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( F , SIZE , N_INPUT_MAX_NUM ) \ uint N_INPUT_MAX_NUM; \ searching = true; \ \ for( uint i = ( SIZE ) - 1 ; searching ; i-- ){ \ \ if( F[i] != zero ){ \ \ N_INPUT_MAX_NUM = i; \ searching = false; \ \ } \ \ } \ \ #define CONVOLUTION_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( J_MIN ) \ const uint j_max = i < N_input_max_0_start_1 ? i - N_input_start_1 : N_input_max_0; \ T sum{ zero }; \ \ for( uint j = J_MIN ; j <= j_max ; j++ ){ \ \ sum += Polynomial::m_f[j] * f.Polynomial::m_f[i - j]; \ \ } \ \ Polynomial::m_f[i] = sum; \ \ #define CONVOLUTION_FOR_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL( J_MIN ) \ const uint j_max = i < N_input_max_0_start_1 ? i - N_input_start_1 : N_input_max_0; \ T& m_fi = answer[i]; \ \ for( uint j = J_MIN ; j <= j_max ; j++ ){ \ \ m_fi += Polynomial::m_f[j] * f.Polynomial::m_f[i - j]; \ \ } \ \ #ifndef CONNECT #define CONNECT( S1 , S2 ) SUBSTITUTE_CONNECT( S1 , S2 ) #define SUBSTITUTE_CONNECT( S1 , S2 ) S1 ## S2 #endif #define DEFINITION_0_OF__FOR_TRUNCATED_POLYNOMIAL( MULTIPLICATION , ACCESS_ENTRY ) \ CONNECT( CONNECT( RETURN_ZERO_FOR_ , MULTIPLICATION ) , _FOR_TRUNCATED_POLYNOMIAL_IF )( Polynomial::m_size == 0 ); \ uint N_output_max = Polynomial::m_size + f.Polynomial::m_size - 2; \ \ if( N_output_max >= m_N ){ \ \ N_output_max = m_N - 1; \ \ } \ \ const uint N_output_lim = N_output_max + 1; \ CONNECT( CONNECT( SET_VECTOR_FOR_ANSWER_OF_ , MULTIPLICATION ) , _FOR_TRUNCATED_POLYNOMIAL )( N_output_lim ); \ \ for( uint i = N_output_max ; searching ; i-- ){ \ \ T sum{ zero }; \ \ for( uint j = 0 ; j <= i ; j++ ){ \ \ sum += ACCESS_ENTRY * f.Polynomial::operator[]( i - j ); \ \ } \ \ CONNECT( CONNECT( SET_SUM_OF_ , MULTIPLICATION ) , _FOR_TRUNCATED_POLYNOMIAL ); \ searching = i > 0; \ \ } \ \ #define DEFINITION_0_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_0_OF__FOR_TRUNCATED_POLYNOMIAL( MULTIPLICATION , Polynomial::m_f[j] ) \ \ #define DEFINITION_0_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_0_OF__FOR_TRUNCATED_POLYNOMIAL( TRUNCATED_MULTIPLICATION_CONST , Polynomial::operator[]( j ) ) \ \ #define DEFINITION_1_OF__FOR_TRUNCATED_POLYNOMIAL( MULTIPLICATION ) \ SET_N_INPUT_START_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( Polynomial::m_f , Polynomial::m_size , N_input_start_0 ); \ CONNECT( CONNECT( RETURN_ZERO_FOR_ , MULTIPLICATION ) , _FOR_TRUNCATED_POLYNOMIAL_IF )( searching ); \ searching = true; \ SET_N_INPUT_START_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( f , f.Polynomial::m_size , N_input_start_1 ); \ \ #define DEFINITION_1_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_1_OF__FOR_TRUNCATED_POLYNOMIAL( MULTIPLICATION ) \ \ #define DEFINITION_1_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_1_OF__FOR_TRUNCATED_POLYNOMIAL( TRUNCATED_MULTIPLICATION_CONST ) \ \ #define DEFINITION_2_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ SET_N_INPUT_MAX_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( Polynomial::m_f , Polynomial::m_size , N_input_max_0 ); \ SET_N_INPUT_MAX_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( f , f.Polynomial::m_size < m_N ? f.Polynomial::m_size : m_N , N_input_max_1 ); \ const uint N_input_max_0_max_1 = N_input_max_0 + N_input_max_1; \ const uint N_input_start_0_start_1 = N_input_start_0 + N_input_start_1; \ \ #define DEFINITION_2_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_2_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ \ #define DEFINITION_3_OF__FOR_TRUNCATED_POLYNOMIAL( MULTIPLICATION ) \ const uint N_input_start_0_max_1 = N_input_start_0 + N_input_max_1; \ const uint N_input_max_0_start_1 = N_input_max_0 + N_input_start_1; \ const uint N_output_max_fixed = N_output_lim_fixed - 1; \ CONNECT( CONNECT( SET_VECTOR_FOR_ANSWER_OF_ , MULTIPLICATION ) , _FOR_TRUNCATED_POLYNOMIAL )( N_output_lim_fixed ); \ \ for( uint i = N_output_max_fixed ; i > N_input_start_0_max_1 ; i-- ){ \ \ CONNECT( CONNECT( CONVOLUTION_FOR_ , MULTIPLICATION ) , _FOR_TRUNCATED_POLYNOMIAL )( i - N_input_max_1 ); \ \ } \ \ searching = true; \ \ for( uint i = N_input_start_0_max_1 < N_output_max_fixed ? N_input_start_0_max_1 : N_output_max_fixed ; searching ; i-- ){ \ \ CONNECT( CONNECT( CONVOLUTION_FOR_ , MULTIPLICATION ) , _FOR_TRUNCATED_POLYNOMIAL )( N_input_start_0 ); \ searching = i > N_input_start_0_start_1; \ \ } \ \ \ #define DEFINITION_3_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_3_OF__FOR_TRUNCATED_POLYNOMIAL( MULTIPLICATION ) \ \ #define DEFINITION_3_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_3_OF__FOR_TRUNCATED_POLYNOMIAL( TRUNCATED_MULTIPLICATION_CONST ) \ \ #define DEFINITION_4_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ uint two_power = FFT_Multiplication_border_1_2; \ uint exponent = FFT_Multiplication_border_1_2_exponent; \ T two_power_inv{ FFT_Multiplication_border_1_2_inv }; \ \ while( N_input_truncated_deg_0_deg_1 >= two_power ){ \ \ two_power *= 2; \ two_power_inv /= 2; \ exponent++; \ \ } \ \ vector f0{ move( FFT( Polynomial::m_f , N_input_start_0 , N_input_max_0 + 1 , 0 , two_power , exponent ) ) }; \ const vector f1{ move( FFT( f.Polynomial::m_f , N_input_start_1 , N_input_max_1 + 1 , 0 , two_power , exponent ) ) }; \ Break( __LINE__ ); \ for( uint i = 0 ; i < two_power ; i++ ){ \ \ f0[i] *= f1[i]; \ \ } \ \ #define DEFINITION_4_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL \ DEFINITION_4_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL \ \ #define DEFINITION_OF_INVERSE_FOR_TRUNCATED_POLYNOMIAL( TYPE , RECURSION ) \ const uint& N = f.GetTruncation(); \ uint power; \ uint power_2 = 1; \ TruncatedPolynomial< TYPE > f_inv{ power_2 , Polynomial< TYPE >::const_one() / f[0] }; \ \ while( power_2 < N ){ \ \ power = power_2; \ power_2 *= 2; \ f_inv.SetTruncation( power_2 ); \ RECURSION; \ \ } \ \ f_inv.SetTruncation( N ); \ return f_inv \ \ #define DEFINITION_OF_EXP_FOR_TRUNCATED_POLYNOMIAL( TYPE , RECURSION ) \ const uint& N = f.GetTruncation(); \ uint power; \ uint power_2 = 1; \ TruncatedPolynomial< TYPE > f_exp{ power_2 , Polynomial< TYPE >::const_one() }; \ \ while( power_2 < N ){ \ Break( __LINE__ ); \ power = power_2; \ power_2 *= 2; \ f_exp.SetTruncation( power_2 ); \ RECURSION; \ \ } \ \ f_exp.SetTruncation( N ); \ return f_exp \ \ inline void Break( const uint& n ) {}; #define DEFINITION_OF_PARTIAL_SPECIALISATION_OF_MULTIPLICATION_OF_TRUNCATED_POLYNOMIAL( TYPE , BORDER_0 , BORDER_1 , BORDER_1_2 , BORDER_1_2_EXPONENT , BORDER_1_2_INV ) \ template <> constexpr const uint FFT_Multiplication_border_0< TYPE > = BORDER_0; \ template <> constexpr const uint FFT_Multiplication_border_1< TYPE > = BORDER_1; \ template <> constexpr const uint FFT_Multiplication_border_1_2< TYPE > = BORDER_1_2; \ template <> constexpr const uint FFT_Multiplication_border_1_2_exponent< TYPE > = BORDER_1_2_EXPONENT; \ template <> constexpr const uint FFT_Multiplication_border_1_2_inv< TYPE > = BORDER_1_2_INV; \ template <> inline TruncatedPolynomial< TYPE >& TruncatedPolynomial< TYPE >::operator*=( const Polynomial< TYPE >& f ) { return TruncatedPolynomial< TYPE >::FFT_Multiplication( f ); } \ \ template <> \ TruncatedPolynomial< TYPE > Inverse( const TruncatedPolynomial< TYPE >& f ) \ { \ \ DEFINITION_OF_INVERSE_FOR_TRUNCATED_POLYNOMIAL( TYPE , f_inv.TruncatedMinus( f_inv.FFT_TruncatedMultiplication_const( f , power , power_2 ).FFT_TruncatedMultiplication( f_inv , power , power_2 ) , power , power_2 ) ); \ \ } \ \ template <> \ TruncatedPolynomial< TYPE > Exp( const TruncatedPolynomial< TYPE >& f ) \ { \ \ DEFINITION_OF_EXP_FOR_TRUNCATED_POLYNOMIAL( TYPE , f_exp.TruncatedMinus( ( TruncatedIntegral( Differential( f_exp ).FFT_TruncatedMultiplication_const( Inverse( f_exp ) , power - 1 , power_2 ) , power ).TruncatedMinus( f , power , power_2 ) ).FFT_TruncatedMultiplication( f_exp , power , power_2 ) , power , power_2 ) ); \ \ } \ \ template class TruncatedPolynomial; template TruncatedPolynomial TruncatedDifferential( const TruncatedPolynomial& f , const uint& N_output_start_plus_one ); template TruncatedPolynomial TruncatedIntegral( const TruncatedPolynomial& f , const uint& N_output_start ); template class TruncatedPolynomial : public Polynomial { friend TruncatedPolynomial TruncatedDifferential( const TruncatedPolynomial& f , const uint& N_output_start_plus_one ); friend TruncatedPolynomial TruncatedIntegral( const TruncatedPolynomial& f , const uint& N_output_start ); private: // m_N == 0でも動くが、その場合はm_size == 1の時にm_size <= m_Nが成り立たなくなることに注意 uint m_N; public: inline TruncatedPolynomial( const uint& N = 0 ); inline TruncatedPolynomial( const TruncatedPolynomial& f ); inline TruncatedPolynomial( const uint& N , const T& t ); TruncatedPolynomial( const uint& N , const Polynomial& f ); inline TruncatedPolynomial( const uint& N , const uint& i , const T& t ); inline TruncatedPolynomial( const uint& N , vector&& f ); // m_Nも代入されることに注意 inline TruncatedPolynomial& operator=( const TruncatedPolynomial& f ); inline TruncatedPolynomial& operator=( const T& t ); inline TruncatedPolynomial& operator=( const Polynomial& f ); // m_Nは変化しないことに注意 inline TruncatedPolynomial& operator+=( const T& t ); inline TruncatedPolynomial& operator+=( const Polynomial& f ); // N_input_lim <= f.size()の場合のみサポート。 // 自身を(N_input_start個の0,f[N_input_start],...,f[N_input_lim-1],0,...)を足した値 mod X^{ m_N }で置き換える。 TruncatedPolynomial& TruncatedPlus( const Polynomial& f , const uint& N_input_start , const uint& N_input_limit ); inline TruncatedPolynomial& operator-=( const T& t ); inline TruncatedPolynomial& operator-=( const Polynomial& f ); // N_input_lim <= f.size()の場合のみサポート。 // 自身を(N_input_start個の0,f[N_input_start],...,f[N_input_lim-1],0,...)を引いた値 mod X^{ m_N }で置き換える。 TruncatedPolynomial& TruncatedMinus( const Polynomial& f , const uint& N_input_start , const uint& N_input_limit ); inline TruncatedPolynomial& operator*=( const T& t ); // m_N > 0の場合のみサポート。 TruncatedPolynomial& operator*=( const Polynomial& f ); // m_N > 0の場合のみサポート。 // 自身をFFTによりf倍 mod X ^ { m_N }を計算した値で置き換える。 // TruncatedPolynomial& FFT_Multiplication( const Polynomial& f ); // m_N > 0かつf != 0の場合のみサポート。 // 自身を(N_input_start個の0,f[N_input_start],...,f[N_input_lim-1],0,...)倍した値 mod X^{ m_N }で置き換える。 TruncatedPolynomial& TruncatedMultiplication( const Polynomial& f , const uint& N_input_start , const uint& N_input_lim ); // m_N > 0かつf != 0の場合のみサポート。 // 自身をFFTにより(N_input_start個の0,f[N_input_start],...,f[N_input_lim-1],0,...)倍した値 mod X^{ m_N }で置き換える。 // TruncatedPolynomial& FFT_TruncatedMultiplication( const Polynomial& f , const uint& N_input_start , const uint& N_input_lim ); // m_N > 0の場合のみサポート。 // 自身をf倍を計算した値をhとし、Polynomial::m_fを長さm_Nの // (N_output_start個の0,h[N_output_start],...,h[N_output_lim - 1],0,...) // を返す。 TruncatedPolynomial TruncatedMultiplication_const( const Polynomial& f , const uint& N_output_start , const uint& N_output_lim ) const; // m_N > 0の場合のみサポート。 // 自身をFFTによりf倍を計算した値をhとし、Polynomial::m_fを長さm_Nの // (N_output_start個の0,h[N_output_start],...,h[N_output_lim - 1],0,...) // を返す。 // TruncatedPolynomial FFT_TruncatedMultiplication_const( const Polynomial& f , const uint& N_output_start , const uint& N_output_lim ) const; inline TruncatedPolynomial& operator/=( const T& t ); inline TruncatedPolynomial& operator/=( const TruncatedPolynomial& t ); inline TruncatedPolynomial& operator%=( const T& t ); inline TruncatedPolynomial operator-() const; inline void SetTruncation( const uint& N ) noexcept; inline const uint& GetTruncation() const noexcept; // N次未満を0に変更。 inline TruncatedPolynomial& TruncateInitial( const uint& N ) noexcept; // N次以上を0に変更。 inline TruncatedPolynomial& TruncateFinal( const uint& N ) noexcept; }; // template inline constexpr const uint FFT_Multiplication_border_0; // template inline constexpr const uint FFT_Multiplication_border_1; // template inline constexpr const uint FFT_Multiplication_border_1_2; // template inline constexpr const uint FFT_Multiplication_border_1_2_exponent; // template inline constexpr const uint FFT_Multiplication_border_1_2_inv; template inline TruncatedPolynomial operator+( const TruncatedPolynomial& f0 , const P& f1 ); template inline TruncatedPolynomial operator-( const TruncatedPolynomial& f ); template inline TruncatedPolynomial operator-( const TruncatedPolynomial& f0 , const P& f1 ); template inline TruncatedPolynomial operator*( const TruncatedPolynomial& f0 , const P& f1 ); template inline TruncatedPolynomial operator/( const TruncatedPolynomial& f0 , const P& f1 ); template inline TruncatedPolynomial operator%( const TruncatedPolynomial& f0 , const T& t1 ); // m_Nが1下がることに注意。 template inline TruncatedPolynomial Differential( const TruncatedPolynomial& f ); // m_Nがi下がることに注意。 template inline TruncatedPolynomial Differential( const uint& i , const TruncatedPolynomial& f ); // m_Nが1下がることに注意。 // N_output_start_plus_one > 0の場合のみサポート。 template TruncatedPolynomial TruncatedDifferential( const TruncatedPolynomial& f , const uint& N_output_start_plus_one ); // m_Nが1上がることに注意。 // Tが標数0またはf.m_N + 1以上の体の場合のみサポート。 template inline TruncatedPolynomial Integral( const TruncatedPolynomial& f ); // m_Nが1上がることに注意。 // N_output_start > 0の場合のみサポート。 template TruncatedPolynomial TruncatedIntegral( const TruncatedPolynomial& f , const uint& N_output_start ); // f[0]が可逆な場合のみサポート。 template TruncatedPolynomial Inverse( const TruncatedPolynomial& f ); // Tが標数0またはf.m_N以上の体でかつf[0] == 0の場合のみサポート。 template TruncatedPolynomial Exp( const TruncatedPolynomial& f ); // Tが標数0またはf.m_N以上の体でかつf[0] == 1の場合のみサポート。 template inline TruncatedPolynomial Log( const TruncatedPolynomial& f ); // Tが標数0またはf.m_N以上の体でかつf[0] == 1の場合のみサポート。 template TruncatedPolynomial Power( const TruncatedPolynomial& f , const T& t ); template inline TruncatedPolynomial::TruncatedPolynomial( const uint& N ) : Polynomial() , m_N( N ) {} template inline TruncatedPolynomial::TruncatedPolynomial( const TruncatedPolynomial& f ) : Polynomial( f ) , m_N( f.m_N ) {} template inline TruncatedPolynomial::TruncatedPolynomial( const uint& N , const T& t ) : Polynomial( t ) , m_N( N ) {} template TruncatedPolynomial::TruncatedPolynomial( const uint& N , const Polynomial& f ) : Polynomial() , m_N( N ) { const uint& size = f.Polynomial::m_size < m_N ? f.Polynomial::m_size : m_N; for( uint i = 0 ; i < size ; i++ ){ Polynomial::m_f.push_back( f.Polynomial::m_f[i] ); Polynomial::m_size++; } } template inline TruncatedPolynomial::TruncatedPolynomial( const uint& N , const uint& i , const T& t ) : Polynomial() , m_N( N ) { if( i < m_N ? t != Polynomial::const_zero() : false ){ Polynomial::operator[]( i ) = t; } } template inline TruncatedPolynomial::TruncatedPolynomial( const uint& N , vector&& f ) : Polynomial( move( f ) ) , m_N( N ) { while( Polynomial::m_size > m_N ){ Polynomial::m_f.pop_back(); Polynomial::m_size--; } } template inline TruncatedPolynomial& TruncatedPolynomial::operator=( const TruncatedPolynomial& f ) { Polynomial::operator=( f ); m_N = f.m_N; return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::operator=( const T& t ) { Polynomial::operator=( t ); return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::operator=( const Polynomial& f ) { return operator=( TruncatedPolynomial( m_N , f ) ); } template inline TruncatedPolynomial& TruncatedPolynomial::operator+=( const T& t ) { Polynomial::operator+=( t ); return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::operator+=( const Polynomial& f ) { return TruncatedPolynomial::TruncatedPlus( f , 0 , f.Polynomial::m_size ); } template TruncatedPolynomial& TruncatedPolynomial::TruncatedPlus( const Polynomial& f , const uint& N_output_start , const uint& N_output_limit ) { const uint& size = N_output_limit < m_N ? N_output_limit < f.Polynomial::m_size ? N_output_limit : f.Polynomial::m_size : m_N < f.Polynomial::m_size ? m_N : f.Polynomial::m_size; const uint& size_min = Polynomial::m_size < size ? Polynomial::m_size : size; for( uint i = N_output_start ; i < size_min ; i++ ){ Polynomial::m_f[i] += f.Polynomial::m_f[i]; } for( uint i = Polynomial::m_size ; i < size ; i++ ){ Polynomial::m_f.push_back( f.Polynomial::m_f[i] ); Polynomial::m_size++; } return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::operator-=( const T& t ) { Polynomial::operator-=( t ); return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::operator-=( const Polynomial& f ) { return TruncatedPolynomial::TruncatedMinus( f , 0 , f.Polynomial::m_size ); } template TruncatedPolynomial& TruncatedPolynomial::TruncatedMinus( const Polynomial& f , const uint& N_output_start , const uint& N_output_limit ) { const uint& size = N_output_limit < m_N ? N_output_limit < f.Polynomial::m_size ? N_output_limit : f.Polynomial::m_size : m_N < f.Polynomial::m_size ? m_N : f.Polynomial::m_size; const uint& size_min = Polynomial::m_size < size ? Polynomial::m_size : size; for( uint i = N_output_start ; i < size_min ; i++ ){ Polynomial::m_f[i] -= f.Polynomial::m_f[i]; } for( uint i = Polynomial::m_size ; i < size ; i++ ){ Polynomial::m_f.push_back( - f.Polynomial::m_f[i] ); Polynomial::m_size++; } return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::operator*=( const T& t ) { Polynomial::operator*=( t ); return *this; } template TruncatedPolynomial& TruncatedPolynomial::operator*=( const Polynomial& f ) { constexpr const uint border_0 = 21; const T& zero = Polynomial::const_zero(); bool searching = true; if( Polynomial::m_size < border_0 && f.Polynomial::m_size < border_0 ){ RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( f.Polynomial::m_size == 0 ); DEFINITION_0_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; } else { DEFINITION_1_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( searching ); DEFINITION_2_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; const uint N_output_lim_fixed = N_input_max_0_max_1 < m_N ? N_input_max_0_max_1 + 1 : m_N; RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( N_input_start_0_start_1 >= m_N ); DEFINITION_3_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; } return *this; } // template // TruncatedPolynomial& TruncatedPolynomial::FFT_Multiplication( const Polynomial& f ) // { // constexpr const uint& border_0 = FFT_Multiplication_border_0; // const T& zero = Polynomial::const_zero(); // bool searching = true; // if( Polynomial::m_size < border_0 && f.Polynomial::m_size < border_0 ){ // RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( f.Polynomial::m_size == 0 ); // DEFINITION_0_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // } else { // DEFINITION_1_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( searching ); // DEFINITION_2_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // const uint N_output_lim_fixed = N_input_max_0_max_1 < m_N ? N_input_max_0_max_1 + 1 : m_N; // RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( N_input_start_0_start_1 >= N_output_lim_fixed ); // const uint N_input_truncated_deg_0_deg_1 = N_input_max_0 - N_input_start_0 + N_input_max_1 - N_input_start_1; // constexpr const uint& border_1 = FFT_Multiplication_border_1; // if( N_input_truncated_deg_0_deg_1 < border_1 ){ // DEFINITION_3_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // } else { // DEFINITION_4_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // Polynomial::m_f = move( IFFT( f0 , 0 , two_power , 0 , 0 , N_output_lim_fixed - N_input_start_0_start_1 , N_input_start_0_start_1 , two_power , two_power_inv , exponent ) ); // Polynomial::m_size = Polynomial::m_f.size(); // } // } // return *this; // } template TruncatedPolynomial& TruncatedPolynomial::TruncatedMultiplication( const Polynomial& f , const uint& N_output_start , const uint& N_output_lim ) { constexpr const uint border_0 = 21; const T& zero = Polynomial::const_zero(); bool searching = true; if( Polynomial::m_size < border_0 && f.Polynomial::m_size < border_0 ){ DEFINITION_0_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; } else { DEFINITION_1_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; DEFINITION_2_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; uint N_output_lim_fixed = N_input_max_0_max_1 < m_N ? N_input_max_0_max_1 + 1 : m_N; if( N_output_lim_fixed > N_output_lim ){ N_output_lim_fixed = N_output_lim; } RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( N_input_start_0_start_1 >= N_output_lim_fixed ); DEFINITION_3_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; } return *this; } // template // TruncatedPolynomial& TruncatedPolynomial::FFT_TruncatedMultiplication( const Polynomial& f , const uint& N_output_start , const uint& N_output_lim ) // { // constexpr const uint& border_0 = FFT_Multiplication_border_0; // const T& zero = Polynomial::const_zero(); // bool searching = true; // if( Polynomial::m_size < border_0 && f.Polynomial::m_size < border_0 ){ // DEFINITION_0_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // } else { // DEFINITION_1_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // DEFINITION_2_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // uint N_output_lim_fixed = N_input_max_0_max_1 < m_N ? N_input_max_0_max_1 + 1 : m_N; // if( N_output_lim_fixed > N_output_lim ){ // N_output_lim_fixed = N_output_lim; // } // RETURN_ZERO_FOR_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL_IF( N_input_start_0_start_1 >= N_output_lim_fixed ); // const uint N_input_truncated_deg_0_deg_1 = N_input_max_0 - N_input_start_0 + N_input_max_1 - N_input_start_1; // constexpr const uint& border_1 = FFT_Multiplication_border_1; // if( N_input_truncated_deg_0_deg_1 < border_1 ){ // DEFINITION_3_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // } else { // DEFINITION_4_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL; // uint N_output_start_shifted; // uint N_output_shift_shifted; // if( N_output_start < N_input_start_0_start_1 ){ // N_output_start_shifted = 0; // N_output_shift_shifted = N_input_start_0_start_1; // } else { // N_output_start_shifted = N_output_start - N_input_start_0_start_1; // N_output_shift_shifted = N_output_start; // } // const uint N_output_lim_shifted = N_output_lim_fixed - N_input_start_0_start_1; // f0 = move( IFFT( f0 , 0 , two_power , 0 , N_output_start_shifted , N_output_lim_shifted , N_output_shift_shifted , two_power , two_power_inv , exponent ) ); // SET_VECTOR_FOR_ANSWER_OF_MULTIPLICATION_FOR_TRUNCATED_POLYNOMIAL( N_output_lim_fixed ); // for( uint i = N_output_start ; i < N_output_lim_fixed ; i++ ){ // Polynomial::m_f[i] = f0[i]; // } // } // } // return *this; // } template TruncatedPolynomial TruncatedPolynomial::TruncatedMultiplication_const( const Polynomial& f , const uint& N_output_start , const uint& N_output_lim ) const { constexpr const uint border_0 = 21; const T& zero = Polynomial::const_zero(); bool searching = true; if( Polynomial::m_size < border_0 && f.Polynomial::m_size < border_0 ){ DEFINITION_0_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; return TruncatedPolynomial( m_N , move( answer ) ); } DEFINITION_1_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; DEFINITION_2_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; uint N_output_lim_fixed = N_input_max_0_max_1 < m_N ? N_input_max_0_max_1 + 1 : m_N; if( N_output_lim_fixed > N_output_lim ){ N_output_lim_fixed = N_output_lim; } RETURN_ZERO_FOR_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL_IF( N_input_start_0_start_1 >= N_output_lim_fixed ); DEFINITION_3_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; return TruncatedPolynomial( m_N , move( answer ) ); } // template // TruncatedPolynomial TruncatedPolynomial::FFT_TruncatedMultiplication_const( const Polynomial& f , const uint& N_output_start , const uint& N_output_lim ) const // { // constexpr const uint& border_0 = FFT_Multiplication_border_0; // const T& zero = Polynomial::const_zero(); // bool searching = true; // if( Polynomial::m_size < border_0 && f.Polynomial::m_size < border_0 ){ // DEFINITION_0_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; // return TruncatedPolynomial( m_N , move( answer ) ); // } // DEFINITION_1_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; // DEFINITION_2_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; // uint N_output_lim_fixed = N_input_max_0_max_1 < m_N ? N_input_max_0_max_1 + 1 : m_N; // if( N_output_lim_fixed > N_output_lim ){ // N_output_lim_fixed = N_output_lim; // } // RETURN_ZERO_FOR_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL_IF( N_input_start_0_start_1 >= N_output_lim_fixed ); // const uint N_input_truncated_deg_0_deg_1 = N_input_max_0 - N_input_start_0 + N_input_max_1 - N_input_start_1; // constexpr const uint& border_1 = FFT_Multiplication_border_1; // if( N_input_truncated_deg_0_deg_1 < border_1 ){ // DEFINITION_3_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; // return TruncatedPolynomial( m_N , move( answer ) ); // } // DEFINITION_4_OF_TRUNCATED_MULTIPLICATION_CONST_FOR_TRUNCATED_POLYNOMIAL; // uint N_output_start_shifted; // uint N_output_shift_shifted; // if( N_output_start < N_input_start_0_start_1 ){ // N_output_start_shifted = 0; // N_output_shift_shifted = N_input_start_0_start_1; // } else { // N_output_start_shifted = N_output_start - N_input_start_0_start_1; // N_output_shift_shifted = N_output_start; // } // const uint N_output_lim_shifted = N_output_lim_fixed - N_input_start_0_start_1; // return TruncatedPolynomial( m_N , move( IFFT( f0 , 0 , two_power , 0 , N_output_start_shifted , N_output_lim_shifted , N_output_shift_shifted , two_power , two_power_inv , exponent ) ) ); // } template inline TruncatedPolynomial& TruncatedPolynomial::operator/=( const T& t ) { Polynomial::operator/=( t ); return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::operator/=( const TruncatedPolynomial& f ) { return operator*=( Inverse( m_N == f.m_N ? f : TruncatedPolynomial( m_N , f ) ) ); } template inline TruncatedPolynomial& TruncatedPolynomial::operator%=( const T& t ) { Polynomial::operator%=( t ); return *this; } template inline TruncatedPolynomial TruncatedPolynomial::operator-() const { return TruncatedPolynomial( m_N ).operator-=( *this ); } template inline void TruncatedPolynomial::SetTruncation( const uint& N ) noexcept { m_N = N; TruncateFinal( m_N ); } template inline const uint& TruncatedPolynomial::GetTruncation() const noexcept { return m_N; } template inline TruncatedPolynomial& TruncatedPolynomial::TruncateInitial( const uint& N ) noexcept { const uint& size = N < Polynomial::m_size ? N : Polynomial::m_size; for( uint i = 0 ; i < size ; i++ ){ Polynomial::m_f[i] = 0; } return *this; } template inline TruncatedPolynomial& TruncatedPolynomial::TruncateFinal( const uint& N ) noexcept { while( Polynomial::m_size > N ){ Polynomial::m_f.pop_back(); Polynomial::m_size--; } return *this; } template inline TruncatedPolynomial operator+( const TruncatedPolynomial& f0 , const P& f1 ) { return TruncatedPolynomial( f0 ).operator+=( f1 ); } template inline TruncatedPolynomial operator-( const TruncatedPolynomial& f ) { return TruncatedPolynomial( f ).operator*=( Polynomial::const_minus_one()); } template inline TruncatedPolynomial operator-( const TruncatedPolynomial& f0 , const P& f1 ) { return TruncatedPolynomial( f0 ).operator-=( f1 ); } template inline TruncatedPolynomial operator*( const TruncatedPolynomial& f0 , const P& f1 ) { return TruncatedPolynomial( f0 ).operator*=( f1 ); } template inline TruncatedPolynomial operator/( const TruncatedPolynomial& f0 , const P& f1 ) { return TruncatedPolynomial( f0 ).operator*=( Inverse( f1 ) ); } template inline TruncatedPolynomial operator%( const TruncatedPolynomial& f0 , const T& t1 ) { return TruncatedPolynomial( f0 ).operator%=( t1 ); } template inline TruncatedPolynomial Differential( const TruncatedPolynomial& f ) { return TruncatedDifferential( f , 1 ); } template inline TruncatedPolynomial Differential( const uint& i , const TruncatedPolynomial& f ) { return i == 0 ? f : Differential( i - 1 , Differential( f ) ); } template TruncatedPolynomial TruncatedDifferential( const TruncatedPolynomial& f , const uint& N_output_start_plus_one ) { if( f.m_N == 0 ){ return TruncatedPolynomial(); } TruncatedPolynomial f_dif{ f.m_N - 1 }; if( N_output_start_plus_one < f.Polynomial::m_size ){ for( uint i = 1 ; i < N_output_start_plus_one ; i++ ){ f_dif.Polynomial::m_f.push_back( 0 ); } for( uint i = N_output_start_plus_one ; i < f.Polynomial::m_size ; i++ ){ f_dif.Polynomial::m_f.push_back( i * f.Polynomial::m_f[i] ); } f_dif.Polynomial::m_size = f.Polynomial::m_size - 1; } return f_dif; } template inline TruncatedPolynomial Integral( const TruncatedPolynomial& f ) { return TruncatedIntegral( f , 1 ); } template TruncatedPolynomial TruncatedIntegral( const TruncatedPolynomial& f , const uint& N_output_start ) { TruncatedPolynomial f_int{ f.m_N + 1 }; if( N_output_start <= f.Polynomial::m_size ){ for( uint i = 0 ; i < N_output_start ; i++ ){ f_int.Polynomial::m_f.push_back( 0 ); } for( uint i = N_output_start ; i <= f.Polynomial::m_size ; i++ ){ f_int.Polynomial::m_f.push_back( f.Polynomial::m_f[i - 1] / T( i ) ); } f_int.Polynomial::m_size = f.Polynomial::m_size + 1; } return f_int; } template TruncatedPolynomial Inverse( const TruncatedPolynomial& f ) { DEFINITION_OF_INVERSE_FOR_TRUNCATED_POLYNOMIAL( T , f_inv.TruncatedMinus( f_inv.TruncatedMultiplication_const( f , power , power_2 ).TruncatedMultiplication( f_inv , power , power_2 ) , power , power_2 ) ); } template TruncatedPolynomial Exp( const TruncatedPolynomial& f ) { DEFINITION_OF_EXP_FOR_TRUNCATED_POLYNOMIAL( T , f_exp.TruncatedMinus( ( TruncatedIntegral( Differential( f_exp ).TruncatedMultiplication_const( Inverse( f_exp ) , power - 1 , power_2 ) , power ).TruncatedMinus( f , power , power_2 ) ).TruncatedMultiplication( f_exp , power ) , power , power_2 ) ); } template inline TruncatedPolynomial Log( const TruncatedPolynomial& f ) { return Integral( Differential( f ) /= f ); } template inline TruncatedPolynomial Power( const TruncatedPolynomial& f , const T& t ) { return Exp( Log( f ) *= t ); } int main() { UNTIE; CIN( int , N ); assert( 1 <= N && N <= 1000 ); CIN( uint , I ); assert( 1 <= I && I <= 1000 ); using P1 = Polynomial; using P2 = TruncatedPolynomial; I++; P2 f{ I , 1 }; uint s; uint a; REPEAT( N ){ cin >> s >> a; assert( 1 <= s && s <= 1000 ); assert( 1 <= a && a <= 1000 ); f = f * P2( I , s , P1( a , 1 ) ) + f; } uint a_max = 0; FOR( i , 0 , I ){ const Polynomial& fi = f[i]; uint size = fi.size(); if( size != 0 ){ size--; if( a_max < size ){ a_max = size; } } } RETURN( a_max ); }