hdu5381(2015多校8)--The sum of gcd(线段树)

题目链接:点击打开链接

题目大意:f(l,r)= ri=lrj=igcd(ai,ai+1....aj),给出初始的n个值,q次询问,每次询问输出f(l,r)的值 

大多都是说莫队算法,没有想出肿么用,,,,本题用两个线段树完成

首先对于任意一个a[i],每次gcd减小至少一半,所以它向后的gcd最多下降log(a[i])次,可以求出对于每一个a[i]来说的gcd相同的各个区间。

用线段树维护一段区间的gcd,可以查询一段[l,r]的gcd的值x,从i开始枚举左边界l,然后用二分查找到gcd相同的区间的右边界r,这个就得到了对于a[i]来说的一段gcd相同的区间,而且下一个区间的左边界就成了r+1,gcd值也变成gcd(x,a[r+1]),继续二分查找该gcd的右边界,,,一直到找到第n个数为止。这样就得到了从i开始的gcd相同的各个区间。

对于要求的f(l,r)来说,如果把所有的gcd(l,,,r)都写在纸上,就会发现f(l,r)是一个三角形的区间和。把询问的l从大到小排序,建一个新的线段树,完成区间修改,区间查询的操作,对于每个询问的l,都把大于等于l的并且还未加到线段树那些gcd区间加入线段树,然后查询[l,r]的和,也就是f(l,r)的值。

#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std ;
#define LL __int64
#define maxn 10010
#define root 1,n,1
#define int_rt int l,int r,int rt
#define lson l,(l+r)/2,rt<<1
#define rson (l+r)/2+1,r,rt<<1|1
struct node{
    int id , l , r , x ;
}p , q[maxn] ;
vector<node> vec[maxn] ;
int n , m , a[maxn] , k ;
LL ans[maxn] ;
int cl[maxn<<2] ;
int c[maxn] ;
int read() {
    int x = 0 ;
    char ch ;
    ch = getchar() ;
    while( ch < '0' || ch > '9' )
        ch = getchar() ;
    while( ch >= '0' && ch <= '9' ) {
        x = x*10 + ch - '0' ;
        ch = getchar() ;
    }
    return x ;
}
int gcd(int a,int b) {
    return b == 0 ? a : gcd(b,a%b) ;
}
int cmp(node a,node b) {
    return a.l > b.l ;
}
void push_up(int rt) {
    cl[rt] = gcd( cl[rt<<1] , cl[rt<<1|1] ) ;
}
void init(int_rt) {
    if( l == r ) {
        cl[rt] = read() ;
        a[l] = cl[rt] ;
        return ;
    }
    init(lson) ;
    init(rson) ;
    push_up(rt) ;
}
int query(int ll,int rr,int_rt) {
    if( ll <= l && rr >= r ) {
        return cl[rt] ;
    }
    int mid = (l+r)/2 , t1 = 0 , t2 = 0 ;
    if( ll <= mid )
        t1 = query(ll,rr,lson) ;
    if( rr > mid )
        t2 = query(ll,rr,rson) ;
    if( t1 && t2 )
        return gcd(t1,t2) ;
    if( t1 ) return t1 ;
    else return t2 ;
}
int search1(int l,int x) {
    int low = l , mid , high = n , temp ;
    while( low <= high ) {
        mid = (low+high)/2 ;
        if( query(l,mid,root) >= x ) {
            low = mid+1 ;
            temp = mid ;
        }
        else
            high = mid-1 ;
    }
    return temp ;
}
LL sum[maxn<<2] , lazy[maxn<<2] ;
void push(int_rt) {
    int mid = (l+r)/2 ;
    sum[rt] = sum[rt<<1] + sum[rt<<1|1] ;
    sum[rt] += lazy[rt<<1]*(mid-l+1) ;
    sum[rt] += lazy[rt<<1|1]*(r-mid) ;
}
void update(int ll,int rr,int x,int_rt) {
    if( ll > r || rr < l ) return ;
    if( ll <= l && rr >= r ) {
        lazy[rt] += x ;
        return ;
    }
    update(ll,rr,x,lson) ;
    update(ll,rr,x,rson) ;
    push(l,r,rt) ;
}
LL getsum(int ll,int rr,LL s,int_rt) {
    if( ll > r || rr < l ) return 0 ;
    if( ll <= l && rr >= r ) {
        return sum[rt] + (lazy[rt]+s)*(r-l+1) ;
    }
    return getsum(ll,rr,s+lazy[rt],lson) + getsum(ll,rr,s+lazy[rt],rson) ;
}
int main() {
    int t , i , j ;
    int l , r , x ;
    t = read() ;
    while( t-- ) {
        memset(c,0,sizeof(c)) ;
        memset(sum,0,sizeof(sum)) ;
        memset(lazy,0,sizeof(lazy)) ;
        n = read() ;
        init(root) ;
        vec[0].clear() ; vec[n+1].clear() ;
        for(i = 1 ; i <= n ; i++) {
            vec[i].clear() ;
            l = r = i ;
            x = a[i] ;
            p.id = i ; p.x = x ;
            p.l = l ; p.r = r ;
            vec[i].push_back(p) ;
            while( r < n ) {
                x = gcd(x,a[r+1]) ;
                l = r+1 ;
                r = search1(i,x) ;
                p.id = i ; p.x = x ;
                p.l = l ; p.r = r ;
                vec[i].push_back(p) ;
            }
        }
        m = read() ;
        for(i = 0 ; i < m ; i++) {
            q[i].l = read() ;
            q[i].r = read() ;
            q[i].id = i ;
        }
        sort(q,q+m,cmp) ;
        l = r = n+1 ;
        int num = 0 ;
        for(i = 0 ; i < m ; i++) {
            while( l > q[i].l ) {
                l-- ;
                for(j = 0 ; j < vec[l].size() ; j++)
                    update(vec[l][j].l,vec[l][j].r,vec[l][j].x,root) ;
            }
            ans[ q[i].id ] = getsum(q[i].l,q[i].r,(LL)0,root) ;
        }
        for(i = 0 ; i < m ; i++)
            printf("%I64d\n", ans[i]) ;
    }
    return 0 ;
}


f(l,r)=ri=lrj=igcd(ai,ai+1....aj)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值