洛谷P6812「MCOI-02」Ancestor 先辈 题解(线段树+差分)

洛谷P6812「MCOI-02」Ancestor 先辈

题目背景

           ▃▆█▇▄▖
       ▟◤▖     ◥█  
   ◢◤    ◢▐       ▐▉
 ▗◤       ▂ ▗▖  ▕ █▎
 ◤ ▗▅▖ ◥▄  ▀▀▀◣ █▊
▐ ▕▎  ◥▖◣◤    ◢██
█◣ ◥▅█▀       ▐███◤
▐█▙▂         ◢███◤
 ◥██◣     ◢▄◤
   ▀██▅▇▀▎▇

题目描述

对于两个序列 \(a,b\),如果满足:

\[\forall i \leq \min(n,m),s.t.\ a_i \leq b_i \]

那么我们称 \(a\)\(b\) 屑(\(n\)\(a\) 的长度,\(m\)\(b\) 的长度)。

如果对于一个序列 \(a\),它比它的所有后缀都屑,那么我们称这个序列为先辈。

给定一个长为 \(n\) 的序列 \(a_i\),共有 \(k\) 次操作,包括以下两种:

  • 1 l r x 区间 \([l,r]\) 加上 \(x\)
  • 2 l r 查询区间 \([l,r]\) 是不是先辈。

输入格式

第一行两个整数 \(n,k\) 代表序列长度与操作数。
第二行 \(n\) 个整数 \(a_i\) 代表数列每一项的值。
接下来 \(k\) 行每行首先三个整数 \(opt,l,r\)

  • 如果 \(opt=1\),接下来一个整数 \(x\) 代表操作 \(1\)
  • 如果 \(opt=2\),代表操作 \(2\)

由于数据故障,\(r\) 可能取到 \(n+1\),请在这类情况下自行令 \(r=n\),谢谢。

输出格式

对于每个操作 \(2\),输出询问结果。

样例 #1

样例输入 #1

7 5
1 9 1 9 8 1 0
2 1 3
1 3 4 9
2 1 4
1 5 6 11
2 2 6

样例输出 #1

No
Yes
No

对于 \(100\%\) 的数据,\(1 \le n,k \le 10^6\)\(|a_i|,|x| \le 10^9\)

分析

维护:

由题可得,先辈序列即为不下降序列,需满足 \(a_i \le a_j\) 并且 \(i \le j\)。判断一个序列是否为先辈我们比较的是一个元素和前一个元素的大小关系。所以我们可以将原序列进行一次差分来对比其是否为先辈序列。
如果差分后的序列中的某个元素小于 \(0\),说明其原序列中对应的元素小于前面的元素,则序列不为先辈。如果差分后序列中的元素均大于等于 \(0\),说明其符合条件。
所以我们可以利用线段树维护某段区间最小值并且判断最小值是否小于 \(0\)


修改:

对于题目的区间加法,由于差分数组在区间加法中的特性,对原序列 \([l,r]\),相当于在差分序列中 \(l\)\(+x\),\(r+1\)\(-x\)。所以我们只需要对这两个点进行单点修改即可。
至此,我们就将原问题转化成了线段树区间查询和单点修改的模板题。

代码:


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 1008600
#define int long long
using namespace std;
int n,m,ch;
int c[MAXN];
int a[MAXN];
struct T{
    int l,r;
    long long val;
}t[MAXN*4];

inline void updata(int node){
    t[node].val=min(t[node<<1].val,t[node<<1|1].val);//维护区间最小值
}
int read(){
   int s = 0,w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){if(ch == '-')w = -1;ch = getchar();}
   while(ch >= '0' && ch <= '9')s = s * 10 + ch - '0',ch = getchar();
   return s * w;
}

inline void build(int l,int r,int node){
    t[node].l=l;
    t[node].r=r;
    if(l==r){
        t[node].val=c[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,node<<1);
    build(mid+1,r,node<<1|1);
    updata(node);
}

inline void change(int p,int node,int k){
    if(t[node].l==t[node].r){
        t[node].val+=k; 
        return;
    }
    
    int mid=(t[node].l+t[node].r)/2;
    if(p<=mid)change(p,node<<1,k);
    else change(p,node<<1|1,k); 
    updata(node);
}


inline long long ask(int l,int r,int node){
    long long ans=0x7fffffff;
    if(l<=t[node].l&&t[node].r<=r){
        return t[node].val;
    }
    int mid=(t[node].l+t[node].r)/2;
    if(l<=mid)ans=min(ans,ask(l,r,node<<1));
    if(r>mid)ans=min(ans,ask(l,r,node<<1|1));
    return ans;
}

signed main(){
    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++){
        cin>>a[i];
    }

    for(int i=1;i<=n;i++){
       c[i]=a[i]-a[i-1];
    }

    int l,r,x,y;
    build(1,n+1,1);

    for(int i=1;i<=m;i++){
        ch=read();l=read();r=read();//若用cin会TLE

        if(ch==1){
            x=read();
            change(l,1,x);
            change(r+1,1,-x);
        }

        if(ch==2){
            if(ask(l+1,r,1)>=0)cout<<"Yes"<<"\n";
            else cout<<"No"<<"\n";
        }
        
    }

    return 0;
}

posted @ 2022-08-14 19:25  DAIANZE  阅读(54)  评论(0编辑  收藏  举报