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