小阳的贝壳(差分+线段树)

小阳的贝壳

题目描述 :
小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 col_i

现在小阳有 3 种操作:

1 l r x:给 [l,r] 区间里所有贝壳的颜色值加上 x 。

2 l r:询问 [l,r] 区间里所有相邻贝壳 颜色值的差(取绝对值) 的最大值(若 l = r 输出 0)。

3 l r :询问 [l,r]区间里所有贝壳颜色值的最大公约数。
输入描述:
第一行输入两个正整数 n,m,分别表示贝壳个数和操作个数。
第二行输入 n 个数 col_i 表示每个贝壳的初始颜色。
第三到第 m + 2 行,每行第一个数为 opt,表示操作编号。接下来的输入的变量与操作编号对应。
输出描述:
共 m 行,对于每个询问(操作 2 和操作 3)输出对应的结果。

1 ≤ n , m ≤ 1 0 5 , 1 ≤ c o l i , x ≤ 1 0 3 , 1 ≤ o p t ≤ 3 , 1 ≤ l ≤ r ≤ n 1≤n,m≤10^5,1≤col_i,x≤10^3 ,1≤opt≤3,1≤l≤r≤n 1n,m105,1coli,x103,1opt3,1lrn

思路: 首先肯定是线段树可以做,单看操作1,想到了线段树的区间修改,但是区间的最大公约数pushdown的过程好像没什么思路。而且我们不用pushdown就尽量不用。另外对于区间修改我们也常用差分来转化,从而避免区间修改,并且此题维护的差值也提示我们使用差分转化。

对于操作1,差分后直接单点修改即可;
对于操作2,从差分的定义出发,在 [ l , r ] 内 [l,r]内 [l,r] ,求 ∣ c o l i − c o l i − 1 ∣ |col_i-col_{i-1}| colicoli1 的最大值其实就是在 [ l + 1 , r ] [l+1,r] [l+1,r]中求 ∣ d i ∣ |d_i| di 的最大值,所以我们直接维护差分数组中元素的绝对值的最大值即可。
对于操作3,先来了解一下 gcd 的一些性质:
g c d ( a , b ) = g c d ( b , a ) ( 交 换 律 ) g c d ( − a , b ) = g c d ( a , b ) g c d ( a , a ) = ∣ a ∣ g c d ( a , 0 ) = ∣ a ∣ g c d ( a , 1 ) = 1 g c d ( a , b ) = g c d ( b , a m o d    b ) g c d ( a , b ) = g c d ( b , a − b ) 如 果 有 附 加 的 一 个 自 然 数 m , 则 : g c d ( m a , m b ) = m ∗ g c d ( a , b ) ( 分 配 律 ) g c d ( a + m b , b ) = g c d ( a , b ) gcd(a,b)=gcd(b,a) (交换律)\\ gcd(-a,b)=gcd(a,b) \\ gcd(a,a)=|a|\\ gcd(a,0)=|a|\\ gcd(a,1)=1\\ gcd(a,b)=gcd(b, a \mod b)\\ gcd(a,b)=gcd(b, a-b)\\ 如果有附加的一个自然数m,\\ 则: gcd(ma,mb)=m * gcd(a,b) (分配律)\\ gcd(a+mb ,b)=gcd(a,b) gcd(a,b)=gcd(b,a)gcd(a,b)=gcd(a,b)gcd(a,a)=agcd(a,0)=agcd(a,1)=1gcd(a,b)=gcd(b,amodb)gcd(a,b)=gcd(b,ab)m,:gcd(ma,mb)=mgcd(a,b)()gcd(a+mb,b)=gcd(a,b)

其中更相减损法: g c d ( a , b ) = g c d ( a , ∣ b − a ∣ ) gcd(a,b)=gcd(a,|b-a|) gcd(a,b)=gcd(a,ba)
在这里插入图片描述
所以对于操作3我们需要维护区间和、区间 gcd 即可(当然维护的这些都是差分数组的信息。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;

struct Node{
	int l,r;
	int sum,Max,gcd;
}tr[N*4];

int n,m;
int w[N],ca[N];

void pushup(int u){
	tr[u].sum = tr[u<<1].sum+tr[u<<1|1].sum;
	tr[u].Max = max(tr[u<<1].Max,tr[u<<1|1].Max);
	tr[u].gcd = __gcd(tr[u<<1].gcd,tr[u<<1|1].gcd);
}

void build(int u,int l,int r)
{
	if(l==r) tr[u] = {l,r,ca[l],abs(ca[l]),abs(ca[l])};		//取绝对值
	else {
		tr[u] = {l,r};
		int mid = l+r>>1;
		build(u<<1,l,mid),build(u<<1|1,mid+1,r);
		pushup(u);
	}
}

void modify(int u,int x,int c)
{
	if(tr[u].l==x&&tr[u].r==x){
		tr[u].sum+=c;		//单点修改差分数组
		tr[u].gcd = tr[u].Max = abs(tr[u].sum);	
	}
	else {
		int mid = tr[u].l+tr[u].r>>1;
		if(x<=mid) modify(u<<1,x,c);
		else modify(u<<1|1,x,c);
		pushup(u);
	}
}

int query_max(int u,int l,int r)
{
	  if(tr[u].l>=l&&tr[u].r<=r) return tr[u].Max;
	  int mid = tr[u].l+tr[u].r>>1;
	  int ans=0;
	  if(l<=mid) ans = max(query_max(u<<1,l,r),ans);
	  if(r>mid) ans = max(query_max(u<<1|1,l,r),ans);
	  return ans;
}

int query_gcd(int u,int l,int r)
{
	if(tr[u].l>=l&&tr[u].r<=r) return tr[u].gcd;
	int mid = tr[u].l+tr[u].r>>1;
	int ans = 0;
	if(l<=mid) ans = __gcd(query_gcd(u<<1,l,r),ans);
	if(r>mid) ans = __gcd(query_gcd(u<<1|1,l,r),ans);
	return ans;
}

int query_sum(int u,int l,int r)
{
	if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
	int mid =tr[u].l+tr[u].r>>1;
	int ans=0;
	if(l<=mid) ans += query_sum(u<<1,l,r);
	if(r>mid) ans += query_sum(u<<1|1,l,r);
	return ans;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	for(int i=1;i<=n;i++) ca[i] = w[i] - w[i-1];  //构造差分数组
	build(1,1,n);
	while(m--){
		int t,l,r,x;
		scanf("%d%d%d",&t,&l,&r);
		if(t==1){
			scanf("%d",&x);
			modify(1,l,x);
			if(r+1<=n) modify(1,r+1,-x);	//不能超过 n 
		}
		else if(t==2){
			if(l==r) printf("0\n");
			else printf("%d\n",query_max(1,l+1,r));  //注意区间
		}
		else printf("%d\n",__gcd(query_sum(1,1,l),query_gcd(1,l+1,r)));   //通过区间第一个元素及差分的gcd求出区间gcd
	}
	return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 我行我“速” 设计师:Amelia_0503 返回首页