CF803G Periodic RMQ Problem

题面翻译

给你一个序列 \(a\) 让你支持

\(1\) \(l\) \(r\) \(x\) 区间赋值

\(2\) \(l\) \(r\) 询问区间最小值

我们觉得这个问题太水了,所以我们不会给你序列\(a\)

而是给你序列一个长度为 \(n\) 的序列 \(b\) ,把 \(b\) 复制粘贴 \(k\) 次就可以得到 \(a\)

\(n\le10^5,k\le10^4,q\le10^5,b_i\le10^9\)

\(1\le l\le r\le n\times k\)

样例 #1

样例输入 #1

3 1
1 2 3
3
2 1 3
1 1 2 4
2 1 3

样例输出 #1

1
3

样例 #2

样例输入 #2

3 2
1 2 3
5
2 4 4
1 4 4 5
2 4 4
1 1 6 1
2 6 6

样例输出 #2

1
5
1

分析

直接得到序列 \(a\) 是不现实的,看到询问次数正常,考虑舍弃掉一些无用的数据。

首先可以想到只保留每次询问的两个端点并离散化,对于这 \(2\times q\) 个端点做线段树。

有这样一组样例:

5 3
5 6 2 3 6
3
2 1 5
1 2 3 4
2 1 5

按照刚才的想法,保留下来的点是 \(5\, 6\, 2\, 6\) ,第一个询问结果是 \(2\) 无误,但是区间推平后的查询结果是 \(4\) 而真正答案是 \(3\)

原因就在于我们保留的时候丢失了 \(3\) 的数据,于是我们能联想到,在我们上面保留的点的基础上,相邻两个已经保留的点之间的最小值也是需要的(但不需要整段的信息,因为不会有询问只询问这一段的一半,或者是修改一半,不然这一段就会被分成两段甚至更多),所以在上面的基础之上,我们把相邻两个点之间的最小值也插入进去做线段树就没问题了。

#include<bits/stdc++.h>                                         
#define int long long
using namespace std;
int n,b,q,tot,cnt=1,all[500005<<2];
int t[500005<<2],tag[500005<<2],a[500005];
int his[500005],now[500005],pos[500005];
struct query{
    int opt,l,r,x;
    void in(){
        cin>>opt>>l>>r;
        if(opt==1)cin>>x;
        all[++tot]=l,all[++tot]=r;
    }
}qq[500005];
void pushup(int num){t[num]=min(t[num<<1],t[num<<1|1]);}
void pushdown(int num,int l,int r){if(tag[num]^-1)tag[num<<1]=t[num<<1]=tag[num<<1|1]=t[num<<1|1]=tag[num],tag[num]=-1;}
void build(int l,int r,int num){
    tag[num]=-1;
    if(l==r)t[num]=a[l];
    else{
        int mid=l+r>>1;
        build(l,mid,num<<1),build(mid+1,r,num<<1|1);
        pushup(num);
    }
}
int check(int x){int y=x%n;return y?y:n;}
void update(int l,int r,int num,int x,int y,int k){
    if(x>r||y<l)return;
    if(x<=l&&r<=y){tag[num]=t[num]=k;return;}
    pushdown(num,l,r);
    int mid=l+r>>1;
    update(l,mid,num<<1,x,y,k),update(mid+1,r,num<<1|1,x,y,k);
    pushup(num);
}
int getmin(int l,int r,int num,int x,int y){
    if(x>r||y<l)return LONG_LONG_MAX;
    if(x<=l&&r<=y)return t[num];
    pushdown(num,l,r);
    int mid=l+r>>1;
    return min(getmin(l,mid,num<<1,x,y),getmin(mid+1,r,num<<1|1,x,y));
}
int get(int l,int r){
    if(r-l+1>=n)return t[1];
    l=check(l),r=check(r);
    return l<=r?getmin(1,n,1,l,r):min(getmin(1,n,1,1,r),getmin(1,n,1,l,n));
}
signed main(){
    cin>>n>>b;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,n,1);
    cin>>q;
    for(int i=1;i<=q;i++)qq[i].in();
    sort(all+1,all+tot+1);
    tot=unique(all+1,all+tot+1)-all-1;
    for(int i=1;i<=q;i++){
        int ql=lower_bound(all+1,all+tot+1,qq[i].l)-all;
        int qr=lower_bound(all+1,all+tot+1,qq[i].r)-all;
        his[ql]=a[check(qq[i].l)],his[qr]=a[check(qq[i].r)];
    }
    a[1]=his[1],pos[1]=1;
    for(int i=1;i<tot;i++){
        if(all[i]+1<=all[i+1]-1)a[++cnt]=get(all[i]+1,all[i+1]-1);
        a[++cnt]=his[i+1],pos[i+1]=cnt;
    }
    build(1,cnt,1);
    for(int i=1;i<=q;i++){
        int ql=pos[lower_bound(all+1,all+tot+1,qq[i].l)-all];
        int qr=pos[lower_bound(all+1,all+tot+1,qq[i].r)-all];
        if(qq[i].opt==1)update(1,cnt,1,ql,qr,qq[i].x);
        else cout<<getmin(1,cnt,1,ql,qr)<<endl;
    }
    return 0;
}
posted @ 2023-06-24 11:44  alex_liu09  阅读(19)  评论(0)    收藏  举报