hdu5930 GCD线段树

70 篇文章 0 订阅
37 篇文章 0 订阅

题目大意:给一个序列,每次换一个位置的值,求每次更新后任意连续的序列的gcd,有几个不同的。序列长度和数字大小<=10W 修改5w次

题解:一开始求出整个每个不同gcd的值可以出现多少次,更新的话对左端所有相同gcd的段求出来,和右端的的段直接暴力更新,由于两端都最多只有logn段,所以复杂度是可以承受的。

#include <bits/stdc++.h>
using namespace std ;
#define maxn 51000
#define maxm 1100000
#define pii pair < int , int >
#define ft first
#define sd second
#define clr( a , x ) memset ( a , x , sizeof a )
#define root 1,1 ,n
#define ls rt<<1
#define rs rt<<1|1
#define lson ls,l,m
#define rson rs,m+1,r
int n , q ;
int a[maxn] ;
int vis[maxm] ;
int val[maxn<<2] ;
vector <pii>G[2] ;
int ans ;
int gcd(int x,int y)
{
    if(x<y) swap(x,y);
    if(y==0) return x;
    return gcd(y,x%y);
}
void update ( int x,int v,int rt,int l,int r )
{
    if ( l == r )
    {
        val[rt] = v ;
        return ;
    }
    int m = l + r >> 1 ;
    if ( x <= m ) update ( x , v , lson ) ;
    else update ( x , v , rson ) ;
    val[rt] = gcd ( val[ls] , val[rs] ) ;
}

int query ( int L , int R , int rt , int l , int r ) {
    if ( L <= l && r <= R ) return val[rt] ;
    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 rt , int l , int r ) {
    if ( r <= R )
    {
        if ( val[rt] % v == 0 ) return pii ( v , l ) ;
        else if ( l == r ) return pii ( gcd ( v , val[rt] ) , l ) ;
    }
    int m = l + r >> 1 ;
    if ( R <= m ) return queryl ( R , v , lson ) ;
    pii tmp = queryl( R , v , rson ) ;
    if ( tmp.ft < v ) return tmp ;
    return queryl ( R , v , lson ) ;
}

pii queryr ( int L , int v , int rt , int l , int r ) {
    if ( L <= l ) {
        if ( val[rt] % v == 0 ) return pii ( v , l ) ;
        else if ( l == r ) return pii ( gcd ( v , val[rt] ) , l ) ;
    }
    int m = l + r >> 1 ;
    if ( m < L ) return queryr ( L , v , rson ) ;
    pii tmp = queryr ( L , v , lson ) ;
    if ( tmp.ft < v ) return tmp ;
    return queryr ( L , v , rson ) ;
}

void getl ( int i )
{
    int nowgcd = a[i] , lastgcd = query ( 1 , i , root ) , x = i ;
    G[0].push_back ( pii ( a[i] , i ) ) ;
    while ( nowgcd != lastgcd )
    {
        pii y = queryl ( x , nowgcd , root ) ;
        G[0].push_back ( y ) ;
        x = y.sd ;
        nowgcd = y.ft ;
    }
    G[0].push_back ( pii ( nowgcd , 0 ) ) ;
}

void getr ( int i )
{
    int nowgcd = a[i] , lastgcd = query ( i , n , root ) , x = i ;
    G[1].push_back ( pii ( a[i] , i ) ) ;
    while ( nowgcd != lastgcd ) {
        pii y = queryr ( x , nowgcd , root ) ;
        G[1].push_back ( y ) ;
        x = y.sd ;
        nowgcd = y.ft ;
    }
    G[1].push_back ( pii ( nowgcd , n + 1 ) ) ;
}

void add ( int x , int y )
{
    if ( vis[x] == 0 ) ++ ans ;
    vis[x] += y ;
    if ( vis[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].sd - G[0][i].sd ;
        int gcdx = G[0][i - 1].ft ;
        for (int j=1; j < G[1].size() ;j++){
            int y = G[1][j].sd - G[1][j - 1].sd ;
            int gcdy = G[1][j - 1].ft ;
            add ( gcd ( gcdx , gcdy ) , f * x * y ) ;
        }
    }
}

void solve ()
{
    ans = 0 ;
    scanf ( "%d%d" , &n , &q ) ;
    for ( int i = 0 ; i < maxm ; ++ i )
    {
        vis[i] = 0 ;
    }
    for ( int i = 1 ; i <= n ; ++ i )
    {
        scanf ( "%d" , &a[i] ) ;
        update ( i , a[i] , root ) ;
        int nowgcd = a[i] , lastgcd = query ( 1 , i , root ) , x = i ;
        while ( nowgcd != lastgcd ) {
            pii y = queryl ( x , nowgcd , root ) ;
            if ( !vis[nowgcd] ) ans++ ;
            vis[nowgcd]+=x-y.sd ;
            x = y.sd ;
            nowgcd = y.ft ;
        }
        if ( !vis[nowgcd] )ans++ ;
        vis[nowgcd] += x ;
    }
    for ( int i = 0 ; i < q ; ++ i )
    {
        int p , v ;
        scanf ( "%d%d" , &p , &v ) ;
        calc ( p , -1 ) ;
        a[p] = v ;
        update ( p , a[p] , root ) ;
        calc ( p , 1 ) ;
        printf ( "%d\n" , ans ) ;
    }
}
int main ()
{
    int cas ;
    scanf("%d",&cas) ;
    for ( int i=1;i<=cas;++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、付费专栏及课程。

余额充值