hdu5381维护区间内所有子区间的gcd之和-线段树
Posted Konjak谷弱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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之和-线段树的主要内容,如果未能解决你的问题,请参考以下文章
HDU - 5381 The sum of gcd(莫队/线段树区间合并)
hdu 5381 The sum of gcd(线段树+gcd)