HDU - 5381 The sum of gcd —— 线段树gcd

题意:

求L到R之间所有子区间gcd的和

思路:

离线处理

先预处理出以每个位置为右端点能得到的所有gcd和最近右端点(用线段树或者由上个点处形成的gcd生成都可),离线之后将扫过的点向左的gcd加入能形成这个gcd的区间,用线段树区间更新区间查询来维护

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,q;
ll num[20010];
struct node
{
	int l,r;
	ll w;
	int tag;
	ll lazy;
};
struct node tree[20010*4];
struct qq
{
	int l,r;
	int id;
	bool operator < (const qq &a)const
	{
		return r<a.r;
	}
};
struct qq ques[20010];
struct nnode
{
	int pos;
	ll w;
	nnode(int p_,ll w_)
	{
		pos=p_;w=w_;
	}
};
vector< vector<struct nnode> >v;
ll ans[20010];
void built(int i,int l,int r)
{
	tree[i].l=l;
	tree[i].r=r;
	tree[i].w=0;
	tree[i].tag=0;
	tree[i].lazy=0;
	if(l==r)
	return;
	int mid=(l+r)>>1;
	built(i<<1,l,mid);
	built(i<<1|1,mid+1,r);
}
void pushdown(int i)
{
	if(tree[i].tag)
	{
		tree[i<<1].tag=tree[i<<1|1].tag=1;
		tree[i<<1].lazy+=tree[i].lazy;
		tree[i<<1|1].lazy+=tree[i].lazy;
		tree[i<<1].w+=(tree[i<<1].r-tree[i<<1].l+1)*tree[i].lazy;
		tree[i<<1|1].w+=(tree[i<<1|1].r-tree[i<<1|1].l+1)*tree[i].lazy;
		tree[i].lazy=0;
		tree[i].tag=0;
	}
}
void updata(int i,int l,int r,ll v)
{
	if(tree[i].l==l&&tree[i].r==r)
	{
		tree[i].w+=v*(tree[i].r-tree[i].l+1);
		tree[i].tag=1;
		tree[i].lazy+=v;
		return;
	}
	int mid=(tree[i].l+tree[i].r)>>1;
	if(r<=mid)
	updata(i<<1,l,r,v);
	else if(l>mid)
	updata(i<<1|1,l,r,v);
	else
	{
		updata(i<<1,l,mid,v);
		updata(i<<1|1,mid+1,r,v);
	}
	tree[i].w=tree[i<<1].w+tree[i<<1|1].w;
}
ll query(int i,int l,int r)
{
	pushdown(i);
	if(tree[i].l==l&&tree[i].r==r)
	{
		return tree[i].w;
	}
	int mid=(tree[i].l+tree[i].r)>>1;
	if(r<=mid)
	return query(i<<1,l,r);
	else if(l>mid)
	return query(i<<1|1,l,r);
	else
	return query(i<<1,l,mid)+query(i<<1|1,mid+1,r);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		v.clear();
		v.resize(n+1);
		for(int i=1;i<=n;i++)
		scanf("%lld",&num[i]);
		for(int i=1;i<=n;i++)
		{
			int pos=i;
			ll now=num[i];
			v[i].push_back(nnode(pos,now));
			for(int j=0;j<v[i-1].size();j++)
			{
				ll k=__gcd(now,v[i-1][j].w);
				if(k!=now)
				{
					v[i].push_back(nnode(v[i-1][j].pos,k));
					now=k;
				}
			}
		}
		built(1,1,n);
		scanf("%d",&q);
		int x,y;
		for(int i=1;i<=q;i++)
		{
			scanf("%d%d",&ques[i].l,&ques[i].r);
			ques[i].id=i;
		}
		sort(ques+1,ques+1+q);
		int i,j=1;
		for(i=1;i<=q;i++)
		{
			while(j<=ques[i].r)
			{
				int pre=1;
				for(int k=v[j].size()-1;k>=0;k--)
				{
					updata(1,pre,v[j][k].pos,v[j][k].w);
					pre=v[j][k].pos+1;
				}
				j++;
			}
			ans[ques[i].id]=query(1,ques[i].l,ques[i].r);
		}
		for(int i=1;i<=q;i++)
		printf("%lld\n",ans[i]);
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值