hdu5381The sum of gcd

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

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)

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

时间: 2024-09-18 14:56:31

hdu5381The sum of gcd的相关文章

hdu 5381 The sum of gcd 莫队+预处理

The sum of gcd Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem Description You have an array A,the length of A is nLet f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj) Input There are multiple test cases. The first line

Sum Of Gcd(hdu 4676)

Sum Of Gcd Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Submission(s): 738    Accepted Submission(s): 333 Problem Description Given you a sequence of number a1, a2, ..., an, which is a permutation of 1...n

HDOJ 5381 The sum of gcd 莫队算法

大神题解: http://blog.csdn.net/u014800748/article/details/47680899 The sum of gcd Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 526    Accepted Submission(s): 226 Problem Description You have an

hdu 5381 The sum of gcd 2015多校联合训练赛#8莫队算法

The sum of gcd Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 23    Accepted Submission(s): 4 Problem Description You have an array ,the length of  is Let Input There are multiple test cases.

HDU 5381 The sum of gcd 莫队暴力

链接 题解链接:http://www.cygmasot.com/index.php/2015/08/15/hdu_5381/ 题意: 给定n长的序列 下面n个数给出这个序列 m个询问 下面m行给出询问的区间. 对于一个询问,输出这个区间内的任意子段的gcd 和. 思路: 因为一个数的gcd只会不变或下降,下降一次至少减半,下降至多32次,所以处理出每个数连续相同的gcd的区间. 然后暴力跑莫队. #pragma comment(linker, "/STACK:1024000000,1024000

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>

HDU 5381 The sum of gcd

对于每个i,求出若干区间[l1,r1],[l2,r2],[l3,r3]...满足gcd(l1~i)~gcd(r1~i)一样,gcd(l2~i)~gcd(r2,i)一样... 则以i为右区间的所有gcd和为sum[i] = (r1 - l1 + 1) * g1 + (r2 - l2 + 1) * g2 + ... 同理求出i右边的一段段同gcd区间[l11,r11],[l22,r22],[l33,r33]... 然后将询问按左区间排序,int p初始设为1,对于p <= i < L,要消除i对所

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