【hdu5381】维护区间内所有子区间的gcd之和-线段树

题意:给定n个数,m个询问,每次询问一个区间内所有连续子区间的gcd的和。n,m<=10^5

题解:

这题和之前比赛的一题很像。我们从小到大枚举r,固定右端点枚举左端点,维护的区间最多只有log段。为什么?以为长区间的gcd肯定是短区间gcd的约数,并且要是不同的话至少要/2,最多那就只有log数值这么多段。还有,相同gcd的区间一定是连续的若干个(想想gcd是怎么求的就知道了)。
线段树每个端点x维护的是以x为左端点,r从1到当前的r的gcd的和。链表维护log段数,然后每次加到线段树里更新。

tle了很久才找到错。清零啊!

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7
  8 typedef long long LL;
  9 const LL N=2*100010;
 10 LL n,m,tl,al,last;
 11 LL val[N],ans[N];
 12 struct node{
 13     LL l,r,last,next;
 14     LL d;
 15 }a[N];
 16 struct trnode{
 17     LL l,r,lc,rc;
 18     LL d,lazy;
 19 }t[2*N];
 20 struct nd{
 21     LL l,r,id;
 22 }q[N];
 23
 24 bool cmp_r(nd x,nd y){return x.r<y.r;}
 25
 26 LL gcd(LL x,LL y)
 27 {
 28     if(y==0) return x;
 29     return gcd(y,x%y);
 30 }
 31
 32 LL bt(LL l,LL r)
 33 {
 34     LL x=++tl;
 35     t[x].l=l;t[x].r=r;
 36     t[x].lc=t[x].rc=0;
 37     t[x].d=0;t[x].lazy=0;
 38     if(l<r)
 39     {
 40         LL mid=(l+r)/2;
 41         t[x].lc=bt(l,mid);
 42         t[x].rc=bt(mid+1,r);
 43     }
 44     return x;
 45 }
 46
 47 void pd(LL x)
 48 {
 49     if(t[x].lazy==0) return ;
 50     LL lc=t[x].lc,rc=t[x].rc;
 51     LL d=t[x].lazy;
 52     t[x].d+=(t[x].r-t[x].l+1)*d;
 53     t[x].lazy=0;
 54     if(lc) t[lc].lazy+=d;
 55     if(rc) t[rc].lazy+=d;
 56 }
 57
 58 void change(LL x,LL l,LL r,LL d)
 59 {
 60     pd(x);
 61     if(t[x].l==l && t[x].r==r) {t[x].lazy+=d;pd(x);return ;}
 62     LL lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)/2;
 63     if(r<=mid) change(lc,l,r,d);
 64     else if(l>mid) change(rc,l,r,d);
 65     else
 66     {
 67         change(lc,l,mid,d);
 68         change(rc,mid+1,r,d);
 69     }
 70     if(lc) pd(lc);
 71     if(rc) pd(rc);
 72     t[x].d=t[lc].d+t[rc].d;
 73 }
 74
 75 LL query(LL x,LL l,LL r)
 76 {
 77     pd(x);
 78     if(t[x].l==l && t[x].r==r) return t[x].d;
 79     LL lc=t[x].lc,rc=t[x].rc,mid=(t[x].l+t[x].r)/2;
 80     if(r<=mid) return query(lc,l,r);
 81     if(l>mid) return query(rc,l,r);
 82     return query(lc,l,mid)+query(rc,mid+1,r);
 83 }
 84
 85 int main()
 86 {
 87     freopen("a.in","r",stdin);
 88     freopen("a.out","w",stdout);
 89     LL T;
 90     scanf("%lld",&T);
 91     while(T--)
 92     {
 93         scanf("%lld",&n);
 94         for(LL i=1;i<=n;i++)
 95         {
 96             scanf("%lld",&val[i]);
 97         }
 98         scanf("%lld",&m);
 99         for(LL i=1;i<=m;i++)
100         {
101             scanf("%lld%lld",&q[i].l,&q[i].r);
102             if(q[i].l>q[i].r) swap(q[i].l,q[i].r);
103             q[i].id=i;
104         }
105         sort(q+1,q+1+m,cmp_r);
106         tl=0;bt(1,n);
107         al=0;last=0;LL p,k=1;
108         for(LL i=1;i<=n;i++) a[i].last=a[i].next=0;//debug 清零 不然下面找a[j].next的时候沿用了上一次的会导致死循环
109         for(LL i=1;i<=n;i++)
110         {
111             a[++al].l=i;a[al].r=i;a[al].d=val[i];
112             a[al].last=last;
113             if(last) a[last].next=al;
114             last=al;
115
116             for(LL j=last;j;j=a[j].last)
117             {
118                 a[j].d=gcd(a[j].d,val[i]);
119             }
120             for(LL j=last;j;j=a[j].last)
121             {
122                 p=a[j].last;
123                 if(p && a[p].d==a[j].d)
124                 {
125                     a[p].r=a[j].r;
126                     a[p].next=a[j].next;
127                     if(a[j].next) a[a[j].next].last=p;
128                     else last=p;
129                 }
130             }
131             // printf("i = %lld\n",i);
132             for(LL j=last;j;j=a[j].last)
133             {
134                 change(1,a[j].l,a[j].r,a[j].d);
135                 // printf("l = %lld  r = %lld  d = %lld\n",a[j].l,a[j].r,a[j].d);
136             }
137             while(k<=m && q[k].r==i)
138             {
139                 ans[q[k].id]=query(1,q[k].l,q[k].r);
140                 k++;
141             }
142         }
143         for(LL i=1;i<=m;i++) printf("%lld\n",ans[i]);
144     }
145     return 0;
146 }
时间: 2024-12-25 11:47:18

【hdu5381】维护区间内所有子区间的gcd之和-线段树的相关文章

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

【GDKOI2016Day1T1-魔卡少女】【拆位】线段树维护区间内所有连续子区间的异或和

题意:给出N个数,M个操作.操作有修改和询问两种,每次修改将一个数改成另一个数,每次询问一个区间的所有连续子区间的异或和.n,m<=100000,ai<=1000 题解: 当年(其实也就是今年)做不出来的题..D1T1啊... 因为ai<=1000,我们可以拆位处理.拆成10个二进制位,每位开1棵线段树. 对于每个节点,维护: d:这段区间的异或和 L[0],L[1]:子区间一定从左端点开始,异或和为0,1的子区间分别有多少个 R[0],R[1]:子区间一定从右端点开始,异或和为0,1的

hdu 5381 The sum of gcd(线段树等差数列区间修改+单点查询)

题意: 给出一个数组a,叫你每次询问如下等式的值. f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj) 解析: 思考了很久终于理解了学长的思路 给你一个序列,这个序列的子序列gcd的个数不会超过logN个(N为每个数字,最大能取到的范围) 因为求gcd是递减的,每次至少除以2,所以gcd的个数只会有logN个. 然后让我们来看看题目要求的是什么. 所有子区间的gcd的和. 比如[1, 5]这个区间可以分解成如下子区间. [1, 1] [1, 2] [1, 3] [1, 4]

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相同的

维护gcd的线段树 补发一波。。。

基础线段树(辣鸡的不行) 发现自己线段树除了会维护加法和乘法就啥也不会了QWQ太菜了 瞎写了一个维护gcd的 首先,gcd(x,y)= gcd(x,y-x) 并且很容易推广到n个数,所以我们可以把原数组差分一下, find时就左右子树大力合并gcd,最后和左端点元素本身取gcd: upd时就直接修改差分数组的端点,同时用树状数组维护原数组变化量:轻松加愉悦. #include<cstdio> #include<iostream> #include<cmath> #def

gcd(线段树离线处理)——HDU 4630

对应HDU题目:点击打开链接 No Pain No Game Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1801    Accepted Submission(s): 770 Problem Description Life is a game,and you lose it,so you suicide. But you can

hdu 5381 The sum of gcd(线段树+gcd)

题目链接:hdu 5381 The sum of gcd 将查询离线处理,按照r排序,然后从左向右处理每个A[i],碰到查询时处理.用线段树维护,每个节点表示从[l,i]中以l为起始的区间gcd总和.所以每次修改时需要处理[1,i-1]与i的gcd值,但是因为gcd值是递减的,成log级,对于每个gcd值记录其区间即可.然后用线段树段修改,但是是修改一个等差数列. #include <cstdio> #include <cstring> #include <vector>

动态求区间K大值(权值线段树)

我们知道我们可以通过主席树来维护静态区间第K大值.我们又知道主席树满足可加性,所以我们可以用树状数组来维护主席树,树状数组的每一个节点都可以开一颗主席树,然后一起做. 我们注意到树状数组的每一棵树都和前一颗树没有关系,so,并不需要可持久化,一个朴素的权值线段树就可以啦. 我们知道普通的线段树是刚开始就把所有的节点都开了,但我们发现并不需要,因为每个点里的操作并不是很多,很大一部分的节点是用不到的,那么我们就可以不开.用Ls 和 Rs 来记左右儿子的地址,随用随开即可. #include<bit

求动区间第k小(离线,线段树)

#include<bits/stdc++.h> const int M = 1e5 + 10 ; int n , m ; int ql , qr , k ; int a[M] , sum [M] , p[M] ; int id = 0 ; void build (int o , int l , int r) { if (l == r) { sum[o] = 1 ; return ; } int mid = (l + r) >> 1 ; build (o << 1 , l