HDU 5726 GCD(線段樹+預處理)
阿新 • • 發佈:2019-01-28
[題意]
給定數列an,每次詢問區間[l,r]上所有數的gcd,並求gcd為此值的區間有多少個?
[分析]
區間gcd用線段樹很好維護
關鍵在於如何求gcd為某值的區間個數
我們固定右端點r,左端點從1移動到r,求得一個gcd序列
容易發現,這個序列是非增的,且可以分為許多段數值相同的區間段
可以證明,這樣的區間段個數不超過log2(n),因為每次gcd值下降時至少降為原來的一半
把右端點右移一位為r+1,用新加入的數a[r+1]去更新所有的區間段的gcd值,再合併gcd值相同的區間段
在這個過程中,區間段的個數始終是log級別的
最後用map統計個數即可
[程式碼]
#include <bits/stdc++.h>
using namespace std ;
const int N = 1e5 ;
typedef long long LL ;
int T , Case , n , q , a[N] ;
int g[N*4] ;
#define lson l,m,o<<1
#define rson m+1,r,o<<1|1
#define root 1,n,1
void build( int l , int r , int o )
{
if( l == r )
{
g[o] = a[l] ;
return ;
}
int m = (l+r)>>1 ;
build(lson) ;
build(rson) ;
g[o] = __gcd(g[o<<1],g[o<<1|1]) ;
}
int query( int L , int R , int l , int r , int o )
{
if( L <= l && R >= r )
return g[o] ;
int m = (l+r)>>1 ;
if( L > m ) return query(L,R,rson) ;
if ( R <= m ) return query(L,R,lson) ;
return __gcd(query(L,R,lson),query(L,R,rson)) ;
}
struct seg
{
int p , g ;
seg() { }
seg( int p , int g ):p(p),g(g) { }
bool operator == ( const seg &rhs ) const
{
return g == rhs.g ;
}
} s[22] ;
map<int,LL> cnt ;
void cal()
{
cnt.clear() ;
int k = 0 ;
for( int i = 1 ; i <= n ; i++ )
{
s[k++] = seg(i,a[i]) ;
for( int j = k-2 ; j >= 0 ; j-- )
s[j].g = __gcd(s[j].g,s[j+1].g) ;
k = unique(s,s+k) - s ;
for( int j = 0 ; j < k-1 ; j++ )
cnt[s[j].g] += s[j+1].p - s[j].p ;
cnt[s[k-1].g] += i+1-s[k-1].p ;
}
}
int main()
{
scanf( "%d" , &T ) ;
while( T-- )
{
scanf( "%d" , &n ) ;
for( int i = 1 ; i <= n ; i++ )
scanf( "%d" , &a[i] ) ;
build(root) ;
cal() ;
scanf( "%d" , &q ) ;
printf( "Case #%d:\n" , ++Case ) ;
while( q-- )
{
int l , r ;
scanf( "%d%d" , &l , &r ) ;
int gcd = query(l,r,root) ;
printf( "%d %I64d\n" , gcd , cnt[gcd] ) ;
}
}
return 0 ;
}