题意:给出一个序列,多次区间询问,算出下面式子的答案
f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj)
做法:连续求gcd的和,看起来就是线段树,由于对于任意数,若再搞任意个其它的数跟它做gcd,考虑下质因数分解,可知最多只有log(n)个不同的gcd,所以对于任意区间最多只有log(n)个不同的gcd,这样就可以用线段树存下来了。
struct Tree
{
ll val;//区间gcd和
int l,r,numl,numr;//numl是这个区间从左往右求gcd的时候有多少个不同的gcd。numr就是从右往左
int sl[32],sr[32],nl[32],nr[32];
//sl储存从左往右开始求gcd的时候出现的不同的gcd,sr就是从右往左
//nl储存从左往右开始求gcd的时候第i个gcd有多少个,nr就是从右往左
};
接下来就可以用求普通区间和的方法做了。
#include<map> #include<string> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> #include<queue> #include<vector> #include<iostream> #include<algorithm> #include<bitset> #include<climits> #include<list> #include<iomanip> #include<stack> #include<set> using namespace std; typedef long long ll; struct Tree { ll val; int l,r,numl,numr; int sl[32],sr[32],nl[32],nr[32]; }tree[40010]; int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } Tree add(Tree a,Tree b) { Tree res; res.val=a.val+b.val; for(int i=0;i<a.numr;i++) for(int j=0;j<b.numl;j++) { int t=gcd(a.sr[i],b.sl[j]); res.val+=ll(t)*a.nr[i]*b.nl[j]; } res.numl=a.numl; for(int i=0;i<res.numl;i++) { res.sl[i]=a.sl[i]; res.nl[i]=a.nl[i]; } for(int i=0;i<b.numl;i++) { int t=gcd(res.sl[res.numl-1],b.sl[i]); if(t==res.sl[res.numl-1]) res.nl[res.numl-1]+=b.nl[i]; else { res.sl[res.numl]=t; res.nl[res.numl++]=b.nl[i]; } } res.numr=b.numr; for(int i=0;i<res.numr;i++) { res.sr[i]=b.sr[i]; res.nr[i]=b.nr[i]; } for(int i=0;i<a.numr;i++) { int t=gcd(res.sr[res.numr-1],a.sr[i]); if(t==res.sr[res.numr-1]) res.nr[res.numr-1]+=a.nr[i]; else { res.sr[res.numr]=t; res.nr[res.numr++]=a.nr[i]; } } return res; } void build(int l,int r,int k) { tree[k].l=l; tree[k].r=r; if(l==r) { scanf("%d",&tree[k].val); tree[k].sl[0]=tree[k].sr[0]=tree[k].val; tree[k].nl[0]=tree[k].nr[0]=tree[k].numl=tree[k].numr=1; return; } int m=l+r>>1; build(l,m,k<<1); build(m+1,r,k<<1|1); tree[k]=add(tree[k<<1],tree[k<<1|1]); tree[k].l=l;tree[k].r=r; } Tree seek(int l,int r,int k) { if(tree[k].l==l&&tree[k].r==r) return tree[k]; int m=tree[k].l+tree[k].r>>1; if(r<=m) return seek(l,r,k<<1); if(l>m) return seek(l,r,k<<1|1); return add(seek(l,m,k<<1),seek(m+1,r,k<<1|1)); } int main() { int T; scanf("%d",&T); while(T--) { int n; scanf("%d",&n); build(1,n,1); int q; scanf("%d",&q); while(q--) { int l,r; scanf("%d%d",&l,&r); printf("%I64d\n",seek(l,r,1).val); } } }
f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj)
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 610 Accepted Submission(s): 265
Problem Description
You have an array A,the
length of A is n
Let f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj)
Input
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
First line has one integers n
Second line has n integers Ai
Third line has one integers Q,the
number of questions
Next there are Q lines,each line has two integers l,r
1≤T≤3
1≤n,Q≤104
1≤ai≤109
1≤l<r≤n
Output
For each question,you need to print f(l,r)
Sample Input
2 5 1 2 3 4 5 3 1 3 2 3 1 4 4 4 2 6 9 3 1 3 2 4 2 3
Sample Output
9 6 16 18 23 10
Author
SXYZ
Source
2015 Multi-University Training Contest 8
f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj)
版权声明:本文为博主原创文章,未经博主允许不得转载。