【HDU】5930 GCD【暴力+线段树二分】

题目链接:GCD

首先以每个点作为右端点的相同区间gcd的左端点构成一个区间,则这些区间最多只有log个。nlogn暴力预处理出每个gcd值的个数。
考虑修改第i个点值对所有gcd值的影响,影响的gcd值肯定是包含i的区间。把所有以i为右端点的gcd值区间取出来,i为左端点的也取出来,取的方法用线段树上二分(比较简单,就不细说了),然后左边和右边最多各logn个,所以logn*logn暴力更新就好了。修改前的减掉,修改后的加回来即可。

#include <bits/stdc++.h>
using namespace std ;

typedef long long LL ;
typedef pair < int , int > pii ;

#define clr( a , x ) memset ( a , x , sizeof a )
#define root 1 , 1 , n
#define ls o << 1
#define rs o << 1 | 1
#define lson ls , l , m
#define rson rs , m + 1 , r

const int MAXN = 50005 ;
const int MAXM = 1000005 ;
int n , q ;
int a[MAXN] ;
int g[MAXM] ;
int T[MAXN * 3] ;
vector < pii > G[2] ;
int ans ;

void update ( int x , int v , int o , int l , int r ) {
    if ( l == r ) {
        T[o] = v ;
        return ;
    }
    int m = l + r >> 1 ;
    if ( x <= m ) update ( x , v , lson ) ;
    else update ( x , v , rson ) ;
    T[o] = __gcd ( T[ls] , T[rs] ) ;
}

int query ( int L , int R , int o , int l , int r ) {
    if ( L <= l && r <= R ) return T[o] ;
    int m = l + r >> 1 ;
    if ( R <= m ) return query ( L , R , lson ) ;
    if ( m <  L ) return query ( L , R , rson ) ;
    return __gcd ( query ( L , R , lson ) , query ( L , R , rson ) ) ;
}

pii queryl ( int R , int v , int o , int l , int r ) {
    if ( r <= R ) {
        if ( T[o] % v == 0 ) return pii ( v , l ) ;
        else if ( l == r ) return pii ( __gcd ( v , T[o] ) , l ) ;
    }
    int m = l + r >> 1 ;
    if ( R <= m ) return queryl ( R , v , lson ) ;
    pii tmp = queryl ( R , v , rson ) ;
    if ( tmp.first < v ) return tmp ;
    return queryl ( R , v , lson ) ;
}

pii queryr ( int L , int v , int o , int l , int r ) {
    if ( L <= l ) {
        if ( T[o] % v == 0 ) return pii ( v , l ) ;
        else if ( l == r ) return pii ( __gcd ( v , T[o] ) , l ) ;
    }
    int m = l + r >> 1 ;
    if ( m < L ) return queryr ( L , v , rson ) ;
    pii tmp = queryr ( L , v , lson ) ;
    if ( tmp.first < v ) return tmp ;
    return queryr ( L , v , rson ) ;
}

void getl ( int i ) {
    int nowg = a[i] , lastg = query ( 1 , i , root ) , x = i ;
    G[0].push_back ( pii ( a[i] , i ) ) ;
    while ( nowg != lastg ) {
        pii y = queryl ( x , nowg , root ) ;
        G[0].push_back ( y ) ;
        x = y.second ;
        nowg = y.first ;
    }
    G[0].push_back ( pii ( nowg , 0 ) ) ;
}

void getr ( int i ) {
    int nowg = a[i] , lastg = query ( i , n , root ) , x = i ;
    G[1].push_back ( pii ( a[i] , i ) ) ;
    while ( nowg != lastg ) {
        pii y = queryr ( x , nowg , root ) ;
        G[1].push_back ( y ) ;
        x = y.second ;
        nowg = y.first ;
    }
    G[1].push_back ( pii ( nowg , n + 1 ) ) ;
}

void add ( int x , int y ) {
    if ( g[x] == 0 ) ++ ans ;
    g[x] += y ;
    if ( g[x] == 0 ) -- ans ;
}

void calc ( int i , int f ) {
    G[0].clear () ;
    G[1].clear () ;
    getl ( i ) ;
    getr ( i ) ;
    for ( int i = 1 ; i < G[0].size () ; ++ i ) {
        int x = G[0][i - 1].second - G[0][i].second ;
        int gx = G[0][i - 1].first ;
        //add ( gx , f * x ) ;
        for ( int j = 1 ; j < G[1].size () ; ++ j ) {
            int y = G[1][j].second - G[1][j - 1].second ;
            int gy = G[1][j - 1].first ;
            add ( __gcd ( gx , gy ) , f * x * y ) ;
        }
    }
}

void solve () {
    ans = 0 ;
    scanf ( "%d%d" , &n , &q ) ;
    for ( int i = 0 ; i < MAXM ; ++ i ) {
        g[i] = 0 ;
    }
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%d" , &a[i] ) ;
        update ( i , a[i] , root ) ;
        int nowg = a[i] , lastg = query ( 1 , i , root ) , x = i ;
        while ( nowg != lastg ) {
            pii y = queryl ( x , nowg , root ) ;
            if ( !g[nowg] ) ++ ans ;
            g[nowg] += x - y.second ;
            x = y.second ;
            nowg = y.first ;
        }
        if ( !g[nowg] ) ++ ans ;
        g[nowg] += x ;
    }
    for ( int i = 0 ; i < q ; ++ i ) {
        int p , v ;
        scanf ( "%d%d" , &p , &v ) ;
        calc ( p , -1 ) ;
        a[p] = v ;
        //printf ( "%d\n" , ans ) ;
        update ( p , a[p] , root ) ;
        calc ( p , 1 ) ;
        printf ( "%d\n" , ans ) ;
    }
}

int main () {
    int T ;
    //freopen ( "gcd.in" , "r" , stdin ) ;
    //freopen ( "gcd2.out" , "w" , stdout ) ;
    scanf ( "%d" , &T ) ;
    for ( int i = 1 ; i <= T ; ++ i ) {
        printf ( "Case #%d:\n" , i ) ;
        solve () ;
    }
    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值