码迷,mamicode.com
首页 > 其他好文 > 详细

hdu5381The sum of gcd

时间:2015-08-17 15:34:58      阅读:219      评论:0      收藏:0      [点我收藏+]

标签:

题意:给出一个序列,多次区间询问,算出下面式子的答案

f(l,r)=ri=lrj=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=lrj=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=lrj=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
1T3
1n,Q104
1ai109
1l<rn
 

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
 

f(l,r)=ri=lrj=igcd(ai,ai+1....aj)

版权声明:本文为博主原创文章,未经博主允许不得转载。

hdu5381The sum of gcd

标签:

原文地址:http://blog.csdn.net/stl112514/article/details/47724901

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!