[2020牛客算法竞赛入门课第九节习题] 小阳的贝壳 线段树好题

题目链接:小阳的贝壳

题意

给你一个长度为n的数列,你能进行三个操作:

  1. 给区间[l,r]所有的值加上x
  2. 询问区间[l,r]内相邻两个数差值绝对值的最大值
  3. 询问[l,r]内所有数的最大公约数

题解

首先如果直接维护的话,步骤2和3很好实现,但是如果给区间增加一个数值,面对步骤3,我们很难在原有基础上改变,只能重新算,这样的时间复杂度和暴力无异。所以需要另辟蹊径。

倘若我们维护一个差分数列,情况就变得简单了许多。
设原数列: a 1 , a 2 , . . . , a n {a_1,a_2,...,a_n} a1,a2,...,an
那么差分数列为:
a 1 , a 2 − a 1 , a 3 − a 2 , . . . . . , a n − a n − 1 {a_1,a_2-a_1,a_3-a_2,.....,a_n-a_{n-1}} a1,a2a1,a3a2,.....,anan1
d 1 , d 2 , d 3 , . . . . . , d n {d_1,d_2,d_3,.....,d_n} d1,d2,d3,.....,dn
步骤1:影响的也只有 d l 和 d r + 1 {d_l和d_{r+1}} dldr+1,点修改即可
步骤2:维护一个区间最大值,然后查询区间[l+1,r]里的最大值。
步骤3:
易知 g c d ( a l , a l + 1 , . . . . . , a r ) = g c d ( a l , ∣ a l + 1 − a l ∣ , ∣ a l + 2 − a l + 1 ∣ , . . . . , ∣ a r − a r − 1 ∣ ) {gcd(a_l,a_{l+1},.....,a_{r})=gcd(a_l,\mid a_{l+1}-a_l \mid,\mid a_{l+2}-a_{l+1} \mid,....,\mid a_{r}-a_{r-1} \mid)} gcd(al,al+1,.....,ar)=gcd(al,al+1al,al+2al+1,....,arar1)

g c d ( a l , ∣ d l + 1 ∣ , ∣ d l + 2 ∣ , . . . . , ∣ d r ∣ ) {gcd(a_l,\mid d_{l+1} \mid,\mid d_{l+2} \mid,....,\mid d_{r} \mid)} gcd(al,dl+1,dl+2,....,dr)

g c d ( ∑ i = 1 l d i , ∣ d l + 1 ∣ , ∣ d l + 2 ∣ , . . . . , ∣ d r ∣ ) {gcd(\sum_{i=1}^ld_i,\mid d_{l+1} \mid,\mid d_{l+2} \mid,....,\mid d_{r} \mid)} gcd(i=1ldi,dl+1,dl+2,....,dr)
由上可知,我们再维护一个区间和和区间内的最大公约数即可。
综上所述,我们只需维护一个区间 d i {d_i } di和、区间 ∣ d i ∣ {\mid d_i \mid} di最大值和区间 ∣ d i ∣ {\mid d_i \mid} di最大公约数

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<deque>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define lowbit(x) x&-x

const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
const int maxm=100+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int col[maxn],d[maxn];
struct Tree
{
    
    
	int summ,maxx,gcdd;
	void init(int summ,int maxx,int gcdd)
	{
    
    
		this->summ = summ;
		this->maxx = maxx;
		this->gcdd = gcdd;
	}
}tree[maxn<<2];

int gcd(int a,int b) {
    
     return b==0 ?a:gcd(b,a%b); }

void pushup(int p)
{
    
    
	tree[p].summ = tree[p*2].summ + tree[p*2+1].summ;
	tree[p].maxx = max(tree[p*2].maxx, tree[p*2+1].maxx);
	tree[p].gcdd = gcd(tree[p*2].gcdd, tree[p*2+1].gcdd);
}
void build(int p,int l,int r)
{
    
    
	if(l==r) {
    
     tree[p].summ=d[l]; tree[p].maxx=abs(d[l]); tree[p].gcdd=abs(d[l]); return ;}
	int mid=l+(r-l)/2;
	build(p*2, l, mid);
	build(p*2+1, mid+1, r);
	pushup(p);
}

void add(int p,int l,int r,int pos,int v)
{
    
    
	if(l==r) {
    
     tree[p].summ+=v, tree[p].maxx=abs(tree[p].summ), tree[p].gcdd=abs(tree[p].summ); return ;}
	int mid=l+(r-l)/2;
	if(pos<=mid) add(p*2, l, mid, pos, v);
	if(pos>mid) add(p*2+1, mid+1, r, pos, v);
	pushup(p);
}

int query_gcd(int p,int l,int r,int ql,int qr)
{
    
    
	if(ql<=l && qr>=r) return tree[p].gcdd;
	int mid=l+(r-l)/2;
	int ans=0;
	if(ql<=mid) ans = gcd(ans,query_gcd(p*2,l,mid,ql,qr));
	if(qr>mid) ans =  gcd(ans,query_gcd(p*2+1,mid+1,r,ql,qr));
	return ans;
}

int query_sum(int p,int l,int r,int ql,int qr)
{
    
    
	if(ql<=l && qr>=r) return tree[p].summ;
	int mid=l+(r-l)/2;
	int ans=0;
	if(ql<=mid) ans += query_sum(p*2,l,mid,ql,qr);
	if(qr>mid) ans += query_sum(p*2+1,mid+1,r,ql,qr);
	return ans;
}

int query_max(int p,int l,int r,int ql,int qr)
{
    
    
	if(ql<=l && qr>=r) return tree[p].maxx;
	int mid=l+(r-l)/2;
	int ans=0;
	if(qr>mid) ans = max(ans,query_max(p*2+1,mid+1,r,ql,qr));
	if(ql<=mid) ans =  max(ans,query_max(p*2,l,mid,ql,qr));
	return ans;
}
int main()
{
    
    
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) 
	{
    
    
		scanf("%d",&col[i]);
		d[i]=col[i]-col[i-1];
	}
	build(1, 1, n);
	while(m--)
	{
    
    
		int op; scanf("%d",&op);
		if(op==1)
		{
    
    
			int l,r,v; scanf("%d%d%d",&l,&r,&v);
			add(1, 1, n, l, v);
			if(r<n) add(1, 1, n, r+1, -v); //注意这里可能r+1>n所以必须特判
		}
		else if(op==2)
		{
    
    
			int l,r; scanf("%d%d",&l,&r);
			printf("%d\n",query_max(1, 1, n, l+1, r));
		}
		else
		{
    
    
			int l,r; scanf("%d%d",&l,&r);
			printf("%d\n",gcd(query_sum(1, 1, n, 1, l),query_gcd(1, 1, n, l+1, r)));
		}
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/108098518