Contest Hunter 4302 Interval GCD 0x40「数据结构进阶」例题 算法进阶指南 线段树+树状数组

描述

给定一个长度为N的数列A,以及M条指令 (N≤5*10^5, M<=10^5),每条指令可能是以下两种之一:
“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。
“Q l r”,表示询问 A[l],A[l+1],…,A[r] 的最大公约数(GCD)。

输入格式

第一行两个整数N,M,第二行N个整数Ai,接下来M行每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

样例输入

5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4

样例输出

1
2
4

数据范围与约定

  • N,M≤2*10^5,l<=r,数据保证任何时刻序列中的数都是不超过2^62-1的正整数。

根据九章算术,gcd(x,y)=gcd(x,y-x),可以进一部扩展gcd(x,y,z)=gcd(x,y-x,z-y),同时n项也成立,

gcd(a[i],a[i+1],a[i+2]...a[n])=gcd(a[i],a[i+1]-a[i],a[i+2]-a[i+1]...a[n]-a[n-1]那么就可以用线段树维护b[i]=a[i]-a[i-1]每次c l r d 

只有a[l]-a[l-1]加d,即b[i]+d;只有a[r+1]-a[r]减d其他的项都不变,故只更新b[l],b[r+1]即可,

a数组可以用     区间更新,单点查询   树状数组维护

Q l r 输出 gcd(a[l]+sum(l),query(1,l+1,r))就行了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<stack>
#include<cstdlib>
#include<cctype>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int len=5e5+5;
struct Node{
	int l,r;
	ll v;//gcd 
	int mid()
	{return (l+r)/2;}
}node[len<<2];
int n,m;
ll arr[len];//用来存初始输入的a数组
ll bet[len];//用来存a[i]-a[i-1]
ll c[len]; 
ll gcd(ll a,ll b)
{
	return b==0?a:gcd(b,a%b);
}
void pushup(int i)
{
	node[i].v=gcd(node[i*2].v,node[i*2+1].v);
}
void built(int i,int l,int r)
{
	node[i].l=l;node[i].r=r;
	if(l==r)
	{
		node[i].v=bet[l];
		return ;
	}
	int mid=node[i].mid();
	built(i*2,l,mid);
	built(i*2+1,mid+1,r);
	pushup(i);
}
ll query(int i,int l,int r)
{
	if(l<=node[i].l&&node[i].r<=r)return llabs(node[i].v);
	int mid=node[i].mid();
	ll v=0;
	if(l<=mid)v=gcd(v,query(i*2,l,r));//0与x的gcd为x 
	if(r>mid)v=gcd(v,query(i*2+1,l,r));
	return llabs(v);//a[i]-a[i-1]可能为负数 
}
void update(int i,int x,ll v)
{
	if(node[i].l==node[i].r)
	{
		node[i].v+=v;
		return ;
	}
	int mid=node[i].mid();
	if(x<=mid)update(i*2,x,v);
	else update(i*2+1,x,v);
	pushup(i);
}
void add(int i,ll v)
{
	for(;i<=n;i+=i&-i)c[i]+=v;
}
ll sum(int i)
{
	ll ans=0;
	for(;i;i-=i&-i)ans+=c[i];
	return ans;	
} 
int main()
{
    scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)scanf("%lld",arr+i),bet[i]=arr[i]-arr[i-1];
	built(1,1,n);//建立维护b[i]=a[i]-[i-1]的线段树
	while(m--)
	{
		char str[3];int l,r;ll v;
		scanf("%s",str);
		if(str[0]=='Q')
		{
			scanf("%d%d",&l,&r);
			ll x=arr[l]+sum(l);
			ll y=query(1,l+1,r);
			printf("%lld\n",gcd(x,y));
		}
		else
		{
			scanf("%d%d%lld",&l,&r,&v);
			add(l,v);add(r+1,-v);
			update(1,l,v);update(1,r+1,-v);
		}	
	} 
}

猜你喜欢

转载自blog.csdn.net/hutwuguangrong/article/details/81218203