CF1422F Boring Queries
\(\text{lcm}\) 的本质是质因子次数的 \(\max\) , 所以只需对每一个质因数考虑贡献
考虑根号分治
-
\(\sqrt{A}\) 以内只有 \(87\) 个质数,可以使用 \(st\) 表解决
-
除掉小质数后,大于 \(\sqrt{A}\) 的质因子只可能有一个,相当于求
\[\prod_{l \le i \le r} a_i[pre_i < l]
\]
其中 \(pre_i\) 为 \(i\) 之前离 \(i\) 最近的等于 \(a_i\) 的位置
可以使用 \(n\) 棵线段树分别维护确定的 \(l\) , 发现相邻 \(l\) 之间只有一位不同,使用主席树即可。
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
const int MAXN = 1e5 , MAXS = 450 , Mod = 1e9 + 7;
int Add( int x , int y ) { x += y; return x >= Mod ? x - Mod : x; }
int Sub( int x , int y ) { x -= y; return x < 0 ? x + Mod : x; }
int Mul( int x , int y ) { return 1ll * x * y % Mod; }
int Qkpow( int x , int po ) { int p = 1; for( ; po ; po >>= 1 , x = Mul( x , x ) ) if( po & 1 ) p = Mul( p , x ); return p; }
int prnum , prime[ MAXS + 5 ]; bool vis[ MAXS + 5 ];
void sieve( ) {
for( int i = 2 ; i <= MAXS ; i ++ ) {
if( !vis[ i ] ) prime[ ++ prnum ] = i;
for( int j = 1 ; j <= prnum && 1ll * i * prime[ j ] <= MAXS ; j ++ ) {
vis[ i * prime[ j ] ] = 1;
if( i % prime[ j ] == 0 ) break;
}
}
}
char mx[ 90 ][ MAXN + 5 ][ 18 ];
int Query( int d , int l , int r ) {
int k = log2( r - l + 1 );
return max( mx[ d ][ l ][ k ] , mx[ d ][ r - ( 1 << k ) + 1 ][ k ] );
}
int n , q , a[ MAXN + 5 ] , pre[ MAXN + 5 ] , nxt[ MAXN + 5 ] , pos[ 2 * MAXN + 5 ];
int siz , rt[ MAXN + 5 ];
struct node { int ls , rs , prd; };
struct Segment_Tree {
node Tree[ 20 * MAXN + 5 ];
#define ls( x ) Tree[ x ].ls
#define rs( x ) Tree[ x ].rs
#define mid ( l + r >> 1 )
void Pushup( int x ) { Tree[ x ].prd = Mul( ls( x ) ? Tree[ ls( x ) ].prd : 1 , rs( x ) ? Tree[ rs( x ) ].prd : 1 ); }
void Build( int &x , int l = 1 , int r = n ) {
x = ++ siz;
if( l == r ) { Tree[ x ].prd = !pre[ l ] ? a[ l ] : 1; return; }
Build( ls( x ) , l , mid ); Build( rs( x ) , mid + 1 , r );
Pushup( x );
}
void Update( int &x , int pos , int val , int l = 1 , int r = n ) {
if( pos < l || pos > r ) return; Tree[ ++ siz ] = Tree[ x ]; x = siz;
if( l == r ) { Tree[ x ].prd = val; return; }
Update( ls( x ) , pos , val , l , mid ); Update( rs( x ) , pos , val , mid + 1 , r );
Pushup( x );
}
int Prod( int x , int ql , int qr , int l = 1 , int r = n ) {
if( !x || r < ql || qr < l ) return 1;
if( ql <= l && r <= qr ) return Tree[ x ].prd;
return Mul( Prod( ls( x ) , ql , qr , l , mid ) , Prod( rs( x ) , ql , qr , mid + 1 , r ) );
}
}Tr;
int main( ) {
sieve();
scanf("%d",&n);
for( int i = 1 ; i <= n ; i ++ ) {
scanf("%d",&a[ i ]);
for( int j = 1 ; j <= prnum ; j ++ )
for( ; a[ i ] % prime[ j ] == 0 ; a[ i ] /= prime[ j ] ) mx[ j ][ i ][ 0 ] ++;
}
for( int d = 1 ; d <= prnum ; d ++ )
for( int j = 1 ; j <= 17 ; j ++ )
for( int i = 1 ; i + ( 1 << j ) - 1 <= n ; i ++ )
mx[ d ][ i ][ j ] = max( mx[ d ][ i ][ j - 1 ] , mx[ d ][ i + ( 1 << j - 1 ) ][ j - 1 ] );
for( int i = 1 ; i <= n ; i ++ ) {
pre[ i ] = pos[ a[ i ] ]; nxt[ pos[ a[ i ] ] ] = i;
pos[ a[ i ] ] = i;
}
Tr.Build( rt[ 0 ] );
for( int i = 1 ; i <= n ; i ++ ) {
rt[ i ] = rt[ i - 1 ];
if( nxt[ i ] ) Tr.Update( rt[ i ] , nxt[ i ] , a[ nxt[ i ] ] );
}
scanf("%d",&q);
for( int i = 1 , l , r , lst = 0 ; i <= q ; i ++ ) {
scanf("%d %d",&l,&r); l = ( l + lst ) % n + 1 , r = ( r + lst ) % n + 1;
if( l > r ) swap( l , r );
lst = 1;
for( int d = 1 ; d <= prnum ; d ++ ) lst = Mul( lst , Qkpow( prime[ d ] , Query( d , l , r ) ) );
lst = Mul( lst , Tr.Prod( rt[ l - 1 ] , l , r ) );
printf("%d\n", lst );
}
return 0;
}