题目链接:点击打开链接
题目大意:f(l,r)= ∑ri=l∑rj=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 ; }