結果

問題 No.2273 一点乗除区間積
ユーザー 👑 p-adicp-adic
提出日時 2023-01-21 14:39:10
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 708 ms / 5,000 ms
コード長 8,721 bytes
コンパイル時間 1,664 ms
コンパイル使用メモリ 99,260 KB
実行使用メモリ 20,556 KB
最終ジャッジ日時 2023-09-06 03:53:48
合計ジャッジ時間 7,559 ms
ジャッジサーバーID
(参考情報)
judge13 / judge12
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 8 ms
11,140 KB
testcase_01 AC 7 ms
11,184 KB
testcase_02 AC 8 ms
11,140 KB
testcase_03 AC 9 ms
11,156 KB
testcase_04 AC 8 ms
11,336 KB
testcase_05 AC 7 ms
11,240 KB
testcase_06 AC 8 ms
11,128 KB
testcase_07 AC 8 ms
11,184 KB
testcase_08 AC 8 ms
11,332 KB
testcase_09 AC 8 ms
11,184 KB
testcase_10 AC 8 ms
11,416 KB
testcase_11 AC 7 ms
11,188 KB
testcase_12 AC 8 ms
11,332 KB
testcase_13 AC 8 ms
11,340 KB
testcase_14 AC 8 ms
11,148 KB
testcase_15 AC 7 ms
11,328 KB
testcase_16 AC 23 ms
11,252 KB
testcase_17 AC 19 ms
11,348 KB
testcase_18 AC 242 ms
11,204 KB
testcase_19 AC 198 ms
14,020 KB
testcase_20 AC 508 ms
17,432 KB
testcase_21 AC 708 ms
20,556 KB
testcase_22 AC 477 ms
17,408 KB
testcase_23 AC 492 ms
17,492 KB
testcase_24 AC 470 ms
17,432 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#pragma GCC optimize ( "O3" )
#pragma GCC optimize( "unroll-loops" )
#pragma GCC target ( "sse4.2,fma,avx2,popcnt,lzcnt,bmi2" )
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdint.h>
#include <cassert>
#include <vector>
using namespace std;

using ll = long long;

#define MAIN main
#define TYPE_OF( VAR ) remove_const<remove_reference<decltype( VAR )>::type >::type
#define UNTIE ios_base::sync_with_stdio( false ); cin.tie( nullptr ) 
#define CEXPR( LL , BOUND , VALUE ) constexpr const LL BOUND = VALUE 
#define CIN( LL , A ) LL A; cin >> A 
#define ASSERT( A , MIN , MAX ) assert( MIN <= A && A <= MAX ) 
#define CIN_ASSERT( A , MIN , MAX ) CIN( TYPE_OF( MAX ) , A ); 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 REPEAT( HOW_MANY_TIMES ) FOR( VARIABLE_FOR_REPEAT , 0 , HOW_MANY_TIMES ) 
#define QUIT return 0 
#define COUT( ANSWER ) cout << ( ANSWER ) << "\n"; 
#define RETURN( ANSWER ) COUT( ANSWER ); QUIT 

template <typename T> inline T Residue( const T& a , const T& p ){ return a >= 0 ? a % p : p - 1 - ( ( - ( a + 1 ) ) % p ); }

template <typename INT>
void SetPrimeFactorisation( const INT& n , vector<INT>& P , vector<INT>& exponent )
{

  INT n_copy = n;
  INT p = 2;

  if( n_copy % p == 0 ){

    P.push_back( p );
    exponent.push_back( 1 );
    INT& exponent_back = exponent.back();
    n_copy /= p;
    
    while( n_copy % p == 0 ){

      exponent_back++;
      n_copy /= p;
      
    }

  }

  p++;

  while( p * p <= n_copy ){

    if( n_copy % p == 0 ){

      P.push_back( p );
      exponent.push_back( 1 );
      INT& exponent_back = exponent.back();
      n_copy /= p;
    
      while( n_copy % p == 0 ){

	exponent_back++;
	n_copy /= p;
      
      }

    }

    p += 2;

  }

  if( n_copy != 1 ){

    P.push_back( n_copy );
    exponent.push_back( 1 );
    
  }
  
  return;
  
}

template <typename INT>
INT EulerFunction( const INT& n , vector<INT>& P , vector<INT>& exponent )
{

  SetPrimeFactorisation( n , P , exponent );
  INT answer = n;
  INT size = P.size();

  for( INT i = 0 ; i < size ; i++ ){

    const INT& P_i = P[i];
    answer -= answer / P_i;
    
  }

  return answer;

}

class MG
{
public:
  // 法B
  static ll g_B;
  // Bの素因数
  static vector<int> g_prime;
  // Bの素因数の指数
  static vector<int> g_exponent;
  // g_primeの長さ
  static int g_size;
  // Bのオイラー関数引く1;
  static ll g_euler_minus;
  // 単位元
  static MG g_e;
  
  // 乗法群(Z/BZ)^xの元
  ll m_n;
  // Bの素因数の生成する乗法群の元
  vector<int> m_factor;
  // 0が形式的に生成する自由群の元
  int m_zero;
  inline MG() : m_n() , m_factor() , m_zero() {}
  inline MG( const MG& n ) : m_n( n.m_n ) , m_factor( n.m_factor ) , m_zero( n.m_zero ) {}
  inline MG( MG&& n ) : m_n( move( n.m_n ) ) , m_factor( move( n.m_factor ) ) , m_zero( move( n.m_zero ) ) {}
  inline MG( ll&& n ) : m_n( move( n ) ) , m_factor( g_size ) , m_zero() { if( m_n == 0 ){ m_n = 1; m_zero++; } else { FOR( i , 0 , g_size ){ int& prime_i = g_prime[i]; int& factor_i = m_factor[i]; while( m_n % prime_i == 0 ){ m_n /= prime_i; factor_i++; } } m_n = Residue( m_n , MG::g_B ); } }
  inline MG& operator=( const MG& n ) { m_n = n.m_n; m_factor = n.m_factor; m_zero = n.m_zero; return *this; }  
  inline MG& operator=( MG&& n ) { m_n = move( n.m_n ); m_factor = move( n.m_factor ); m_zero = move( n.m_zero ); return *this; }  
};

ll MG::g_B{};
vector<int> MG::g_prime{};
vector<int> MG::g_exponent{};
int MG::g_size{};
ll MG::g_euler_minus{ 1 };
MG MG::g_e{};

// 乗法
inline MG op( const MG& n0 , const MG& n1 ) { MG answer{ n0 }; if( ! n0.m_factor.empty() && ! n1.m_factor.empty() ){ ( answer.m_n *= n1.m_n ) %= MG::g_B; FOR( i , 0 , MG::g_size ){ answer.m_factor[i] += n1.m_factor[i]; }; answer.m_zero += n1.m_zero; } return answer; }
// 単位元
inline const MG& e() { return MG::g_e; }
// 逆元
inline MG inv( const MG& n ) { MG answer{ n }; answer.m_n = 1; ll power = n.m_n; ll exponent = MG::g_euler_minus; while( exponent != 0 ){ if( ( exponent & 1 ) == 1 ){ ( answer.m_n *= power ) %= MG::g_B; } ( power *= power ) %= MG::g_B; exponent >>= 1; } FOR( i , 0 , MG::g_size ){ answer.m_factor[i] *= -1; } answer.m_zero *= -1; return answer; }

#define TEMPLETE_ARGUMENTS_FOR_BIT typename T , T m_T(const T&,const T&) , const T& e_T() , T i_T(const T&) , int N

// 可換群(T,m_T:T^2->T,e_T:1->T,i_T:T->T)と非負整数Nをパラメータとする。
// ただしi_Tを使うのはSetとIntervalSumのみなので、
// AddとInitialSegmentSumしか使わない場合は
// i_Tを好きに設定して(T,m_T,e_T)をモノイドとして良い。
template <TEMPLETE_ARGUMENTS_FOR_BIT>
class AbstractBIT
{
private:
  T m_fenwick[N + 1];
  
public:
  static const T& g_e;

  inline AbstractBIT();
  AbstractBIT( const T ( & a )[N] );

  inline void Set( const int& i , const T& n );

  inline AbstractBIT<T,m_T,e_T,i_T,N>& operator+=( const T ( & a )[N] );
  void Add( const int& i , const T& n );

  T InitialSegmentSum( const int& i_final );
  inline T IntervalSum( const int& i_start , const int& i_final );
  
};


template <TEMPLETE_ARGUMENTS_FOR_BIT> inline const T& AbstractBIT<T,m_T,e_T,i_T,N>::g_e = e_T();

template <TEMPLETE_ARGUMENTS_FOR_BIT> inline AbstractBIT<T,m_T,e_T,i_T,N>::AbstractBIT() : m_fenwick() { const T& e = g_e; for( int i = 0 ; i <= N ; i++ ){ m_fenwick[i] = e; } }
template <TEMPLETE_ARGUMENTS_FOR_BIT>
AbstractBIT<T,m_T,e_T,i_T,N>::AbstractBIT( const T ( & a )[N] ) : m_fenwick()
{

  for( int j = 1 ; j <= N ; j++ ){

    T& fenwick_j = m_fenwick[j];
    int i = j - 1;
    fenwick_j = a[i];
    int i_lim = j - ( j & -j );

    while( i != i_lim ){

      fenwick_j = m_T( fenwick_j , m_fenwick[i] );
      i -= ( i & -i );

    }

  }

}

template <TEMPLETE_ARGUMENTS_FOR_BIT> inline void AbstractBIT<T,m_T,e_T,i_T,N>::Set( const int& i , const T& n ) { Add( i , m_T( i_T( IntervalSum( i , i ) ) , n ) ); }

template <TEMPLETE_ARGUMENTS_FOR_BIT> inline AbstractBIT<T,m_T,e_T,i_T,N>& AbstractBIT<T,m_T,e_T,i_T,N>::operator+=( const T ( & a )[N] ) { for( int i = 0 ; i < N ; i++ ){ Add( i , a[i] ); } return *this; }

template <TEMPLETE_ARGUMENTS_FOR_BIT>
void AbstractBIT<T,m_T,e_T,i_T,N>::Add( const int& i , const T& n )
{
  
  int j = i + 1;

  while( j <= N ){

    T& m_fenwick_j = m_fenwick[j];
    m_fenwick_j = m_T( m_fenwick_j , n );
    j += ( j & -j );

  }

  return;
  
}

template <TEMPLETE_ARGUMENTS_FOR_BIT> 
T AbstractBIT<T,m_T,e_T,i_T,N>::InitialSegmentSum( const int& i_final )
{

  T sum = g_e;
  int j = ( i_final < N ? i_final : N - 1 ) + 1;

  while( j > 0 ){

    sum = m_T( sum , m_fenwick[j] );
    j -= j & -j;
    
  }

  return sum;
  
}

template <TEMPLETE_ARGUMENTS_FOR_BIT> inline T AbstractBIT<T,m_T,e_T,i_T,N>::IntervalSum( const int& i_start , const int& i_final ) { return m_T( i_T( InitialSegmentSum( i_start - 1 ) ) , InitialSegmentSum( i_final ) ); }

int MAIN()
{
  UNTIE;
  CEXPR( int , bound_NQ , 100000 );
  CIN_ASSERT( N , 1 , bound_NQ );
  CEXPR( int , bound_B , 1000000000 );
  CIN_ASSERT( B , 1 , bound_B );
  MG::g_B = B;
  MG::g_euler_minus = EulerFunction( B , MG::g_prime , MG::g_exponent ) - 1;
  MG::g_size = MG::g_prime.size();
  MG::g_e.m_n = 1;
  MG::g_e.m_factor = vector<int>( MG::g_size );
  CIN_ASSERT( Q , 1 , bound_NQ );
  CEXPR( ll , bound_Am , 1000000000000000000 );
  static MG A_prep[bound_NQ] = {};
  FOR( i , 0 , N ){
    CIN_ASSERT( Ai , 0 , bound_Am );
    A_prep[i] = MG( move( Ai ) );
  }
  AbstractBIT<MG,op,e,inv,bound_NQ> A{ A_prep };
  N--;
  MG B_inv = MG( B );
  FOR( i , 0 , MG::g_size ){
    B_inv.m_factor[i] *= -1;
  }
  REPEAT( Q ){
    CIN_ASSERT( j , 0 , N );
    CIN_ASSERT( m , 0 , bound_Am );
    CIN_ASSERT( l , 0 , N );
    CIN_ASSERT( r , l , N );
    MG A_j = A.IntervalSum( j , j );
    if( A_j.m_zero == 0 ){
      if( m == 0 ){
	A.Set( j , MG( move( m ) ) );
      } else {
	bool divisible = m == MG::g_B ;
	if( divisible ){
	  FOR( i , 0 , MG::g_size ){
	    if( A_j.m_factor[i] < MG::g_exponent[i] ){
	      divisible = false;
	      break;
	    }
	  }
	}
	if( divisible ){
	  A.Add( j , B_inv );	  
	} else {
	  A.Add( j , MG( move( m ) ) );
	}
      }
    }
    MG A_lr = A.IntervalSum( l , r );
    if( A_lr.m_zero > 0 ){
      COUT( 0 );
    } else {
      ll answer = A_lr.m_n;
      FOR( i , 0 , MG::g_size ){
	ll power = MG::g_prime[i];
	int factor_i = A_lr.m_factor[i];
	while( factor_i != 0 ){
	  if( ( factor_i & 1 ) == 1 ){
	    ( answer *= power ) %= MG::g_B;
	  }
	  ( power *= power ) %= MG::g_B;
	  factor_i >>= 1;
	}
      }
      COUT( answer );
    }
  }
  QUIT;
}
0