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

题目链接:点击打开链接

题目大意: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)

版权声明:转载请注明出处:http://blog.csdn.net/winddreams

时间: 2024-10-11 06:00:55

hdu5381(2015多校8)--The sum of gcd(线段树)的相关文章

多校训练hdu --Nice boat(线段树,都是泪)

Nice boat Time Limit: 30000/15000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 47 Accepted Submission(s): 10 Problem Description There is an old country and the king fell in love with a devil. The devil always ask

Codeforces 85D Sum of Medians(线段树)

85D Sum of Medians 题目链接 题意:一个集合有添加,删除元素,每次查询输出集合位置为i % 5 == 3的位置和 思路:线段树,线段树记录下% 5 == 0, 1, 2, 3, 4的和,并且记录一个mov表示右移多少,每次添加一个值的时候,就当前位置之后的一整段位置都要右移一个单位,这样去搞线段树维护一下即可 代码: #include <cstdio> #include <cstring> #include <cstdlib> #include <

【Educational Codeforces Round 37】F. SUM and REPLACE 线段树+线性筛

题意 给定序列$a_n$,每次将$[L,R]$区间内的数$a_i$替换为$d(a_i)$,或者询问区间和 这题和区间开方有相同的操作 对于$a_i \in (1,10^6)$,$10$次$d(a_i)$以内肯定可以最终化为$1$或者$2$,所以线段树记录区间最大值和区间和,$Max\le2$就返回,单点暴力更新,最后线性筛预处理出$d$ 时间复杂度$O(m\log n)$ 代码 #include <bits/stdc++.h> using namespace std; typedef long

codeforces CF920F SUM and REPLACE 线段树 线性筛约数

$ \Rightarrow $ 戳我进CF原题 F. SUM and REPLACE time limit per test: 2 seconds memory limit per test: 256 megabytes input: standard input output: standard output Let $ D(x) $ be the number of positive divisors of a positive integer $ x $ . For example, $

[email&#160;protected] [307] Range Sum Query - Mutable / 线段树模板

Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. The update(i, val) function modifies nums by updating the element at index i to val. Example: Given nums = [1, 3, 5] sumRange(0, 2) -> 9 update(1, 2

HDU6315 Naive Operations(多校第二场1007)(线段树)

Naive Operations Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 502768/502768 K (Java/Others)Total Submission(s): 3636    Accepted Submission(s): 1612 Problem Description In a galaxy far, far away, there are two integer sequence a and b of l

2018多校第十场 HDU 6430 线段树合并

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6430 题意:一棵树上每个节点权值为v[i],每个节点的heard值是:以它为LCA的两个节点的GCD的最大值,要求输出每个节点的heard值. 题解:权值范围是[1, 1e5],1e5内数因子最多不超过200个,对每个点建一颗线段树,维护每个点的因子,dfs过程中由下往上合并线段树并更新答案. 1 #include <bits/stdc++.h> 2 using namespace std; 3

2019杭电多校第二场hdu6602 Longest Subarray(线段树)

Longest Subarray 题目传送门 解题思路 本题求一个最大的子区间,满足区间内的数字要么出现次数大于等于k次,要么没出现过.给定区间内的数字范围是1~c. 如果r为右边界,对于一种数字x,满足条件的左边界l的范围是r左边第一个x出现的位置+1(即这段区间内没有出现过x,如果x在1~r内都没有出现过,那么1~r自然都是l的合法范围),以及1到从右往左数数第k个x出现的位置(即这段区间内的x出现次数大于等于k).所以我们只要找到同时是c种数字的合法左边界的位置中最小的,然后枚举所有的i作

Educational Codeforces Round 72 (Rated for Div. 2)E. Sum Queries?(线段树区间合并)

https://codeforc.es/contest/1217/problem/E 建立9棵数位线段树维护区间最小值和次小值,建议用struct建树方便进行区间合并 1 #define bug(x) cout<<#x<<" is "<<x<<endl 2 #define IO std::ios::sync_with_stdio(0) 3 #include <bits/stdc++.h> 4 #define iter ::it

CF1217E Sum Queries? (线段树)

完了,前几天才说 edu 的 DEF 都不会,现在打脸了吧 qwq 其实在刚说完这句话 1min 就会了 D,3min 就会了 E 发现,对于大小 \(\ge 3\) 的不平衡集合,它至少有一个大小为 \(2\) 的子集是不平衡的. 证明,发现对于大小为 \(2\) 的集合,平衡当且仅当两数的数位交为空(对于任意一位,至多一个数在这一位上不是 \(0\)). 反证一波,如果大集合没有大小为 \(2\) 的不平衡集合,那么任意两数的数位交都为空,那么大集合也是平衡的,矛盾了. 所以,只需要考虑大小