HDU - 5381 The sum of gcd(莫队/线段树区间合并)

Posted Frozen_Guardian

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 5381 The sum of gcd(莫队/线段树区间合并)相关的知识,希望对你有一定的参考价值。

题目链接:点击查看

题目大意:给出一个长度为 n n n 的序列,再给出 m m m 次询问,每次询问需要回答区间 [ L , R ] [L,R] [L,R] 内所有子区间的 g c d gcd gcd 之和。更具体的,对于询问 [ L , R ] [L,R] [L,R],输出 ∑ l = L R ∑ r = l R gcd ⁡ a l , a l + 1 , ⋯   , a r \\sum\\limits_l=L^R\\sum\\limits_r=l^R\\gcd\\a_l,a_l+1,\\cdots,a_r\\ l=LRr=lRgcdal,al+1,,ar

题目分析:首先需要知道 g c d gcd gcd 的一个性质,因为 g c d gcd gcd 运算每次至少除以二,所以一个连续的区间内,最多有 l o g n logn logn 个不同的 g c d gcd gcd 取值。也就是说一个连续的区间可以被分成 l o g n logn logn 个连续的子区间。

本题离线的话是可以直接挂莫队的,只需要预处理一下每个前缀和后缀的所有 g c d gcd gcd 的不同取值,在扩展区间和收缩区间时就可以将 O ( n ) O(n) O(n) 级别的子区间压缩成 O ( l o g n ) O(logn) O(logn) 个子区间了,时间复杂度是 O ( n l o g n n ) O(nlogn\\sqrtn) O(nlognn )

全网几乎都是莫队离线写的,但我个人感觉用线段树在线也是可以做的,具体难点就是在线段树的区间合并上,考虑如何将左区间和右区间进行合并。还是需要用到上面的性质,可以将左右区间分别拆成 l o g n logn logn 个连续的区间,然后 O ( l o g 2 n ) O(log^2n) O(log2n) 去暴力合并即可,总的复杂度是 O ( n l o g 3 n ) O(nlog^3n) O(nlog3n)

代码:
线段树

// Problem: The sum of gcd
// Contest: Virtual Judge - HDU
// URL: https://vjudge.net/problem/HDU-5381
// Memory Limit: 65 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map>
#define lowbit(x) (x&-x)
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
template<typename T>
inline void read(T &x)

	T f=1;x=0;
	char ch=getchar();
	while(0==isdigit(ch))if(ch=='-')f=-1;ch=getchar();
	while(0!=isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	x*=f;

template<typename T>
inline void write(T x)

	if(x<0)x=~(x-1);putchar('-');
    if(x>9)write(x/10);
    putchar(x%10+'0');

const int inf=0x3f3f3f3f;
const int N=1e4+100;
struct Node 
	int l,r;
	LL sum;
tree[N<<2];
int a[N],st[N][20],Log[N];
void get_st(int n) 
	for(int i=1;i<=n;i++) Log[i]=log2(i);
	for(int i=1;i<=n;i++) st[i][0]=a[i];
	for(int i=1;i<=19;i++) for(int j=1;j+(1<<i)-1<=n;j++) st[j][i]=__gcd(st[j][i-1],st[j+(1<<(i-1))][i-1]);

int ask(int l,int r) 
	int k=Log[r-l+1];
	return __gcd(st[l][k],st[r-(1<<k)+1][k]);

int get_L(int l,int r,int R,int val) //[l,r]找到gcd[i:R]==val的位置
	int ans=-1;
	while(l<=r) 
		int mid=(l+r)>>1;
		if(ask(mid,R)==val) r=mid-1,ans=mid;
		else l=mid+1;
	
	return ans;

int get_R(int l,int r,int L,int val) //[l,r]找到gcd[L:i]==val的位置
	int ans=-1;
	while(l<=r) 
		int mid=(l+r)>>1;
		if(ask(L,mid)==val) l=mid+1,ans=mid;
		else r=mid-1;
	
	return ans;

Node merge(const Node &a,const Node& b) 
	Node ans;
	ans.l=a.l,ans.r=b.r;
	ans.sum=a.sum+b.sum;
	int l=ans.l,r=ans.r,mid=a.r;
	vector<int>L,R;
	L.push_back(mid+1),R.push_back(mid);
	int pos=mid;
	while(pos>=l) 
		pos=get_L(l,pos,mid,ask(pos,mid));
		L.push_back(pos);
		pos--;
	
	pos=mid+1;
	while(pos<=r) 
		pos=get_R(pos,r,mid+1,ask(mid+1,pos));
		R.push_back(pos);
		pos++;
	
	for(int i=1;i<(int)L.size();i++) for(int j=1;j<(int)R.size();j++) 
		ans.sum+=1LL*(L[i-1]-L[i])*(R[j]-R[j-1])*ask(L[i],R[j]);
	
	return ans;

void pushup(int k) 
	tree[k]=merge(tree[k<<1],tree[k<<1|1]);

void build(int k,int l,int r) 
	tree[k]=l,r,0;
	if(l==r) tree[k].sum=a[l];return;
	int mid=(l+r)>>1;
	build(k<<1,l,mid),build(k<<1|1,mid+1,r);
	pushup(k);

Node query(int k,int l,int r) 
	if(tree[k].l>=l&&tree[k].r<=r) return tree[k];
	int mid=(tree[k].l+tree[k].r)>>1;
	if(r<=mid) return query(k<<1,l,r);
	else if(l>mid) return query(k<<1|1,l,r);
	else return merge(query(k<<1,l,r),query(k<<1|1,l,r));

int main()

#ifndef ONLINE_JUDGE
//	freopen("data.in.txt","r",stdin);
//	freopen("data.out.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	int w;
	cin>>w;
	while(w--) 
		int n,m;
		scanf("%d",&n);
		for(int i=1;i<=n;i++) 
			scanf("%d",a+i);
		
		get_st(n);
		build(1,1,n);
		scanf("%d",&m);
		while(m--) 
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%lld\\n",query(1,l,r).sum);
		
	
	return 0;

莫队

// Problem: The sum of gcd
// Contest: Virtual Judge - HDU
// URL: https://vjudge.net/problem/HDU-5381
// Memory Limit: 65 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast","inline","-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include以上是关于HDU - 5381 The sum of gcd(莫队/线段树区间合并)的主要内容,如果未能解决你的问题,请参考以下文章

Hdu5381 the sum of gcd(莫队)

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

HDU - 5381 The sum of gcd(莫队/线段树区间合并)

HDU - 5381 The sum of gcd(莫队/线段树区间合并)

(预处理+莫队算法)HDU - 5381 The sum of gcd

HDOJ 5381 The sum of gcd 莫队算法