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=L∑Rr=l∑Rgcdal,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(莫队/线段树区间合并)的主要内容,如果未能解决你的问题,请参考以下文章
hdu 5381 The sum of gcd 2015多校联合训练赛#8莫队算法
HDU - 5381 The sum of gcd(莫队/线段树区间合并)
HDU - 5381 The sum of gcd(莫队/线段树区间合并)