首页 > 代码库 > 【hdu5381】维护区间内所有子区间的gcd之和-线段树

【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 }

 

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