結果
| 問題 |
No.2273 一点乗除区間積
|
| コンテスト | |
| ユーザー |
👑 |
| 提出日時 | 2023-01-21 14:39:10 |
| 言語 | C++17(gcc12) (gcc 12.3.0 + boost 1.87.0) |
| 結果 |
AC
|
| 実行時間 | 653 ms / 5,000 ms |
| コード長 | 8,721 bytes |
| コンパイル時間 | 5,368 ms |
| コンパイル使用メモリ | 124,852 KB |
| 最終ジャッジ日時 | 2025-02-10 06:09:22 |
|
ジャッジサーバーID (参考情報) |
judge2 / judge3 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 25 |
ソースコード
#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;
}