【NOI2017模拟6.23】回转寿司

题目大意

有n个人排成一个圆环,每个人初始有一个数字a[i],有m轮操作,每轮操作选择一段连续的人并给出一个数字x,按顺时针顺序比较x和a[i],如果x小于a[i],就那么交换a[i]和x
1n4×105
1m2.5104

解法

考虑分块,那么对于一个块来说,如果当前某个数x进去了,那么出来的要不是x要不就是这个块的最大值,所以对每个块维护一个大根堆。
而对于块内的重构,由于本题有很好的性质:标记的处理和a[]的变化是具有对称性的,就是说重构时只要维护一个对于标记的小根堆,然后从左到右扫一遍。

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<bitset>
#include<map>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

typedef long long LL;
typedef double db;

int get(){
    char ch;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
    if (ch=='-'){
        int s=0;
        while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
        return -s;
    }
    int s=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
    return s;
}

const int N = 400010;
const int Q = 25010;
const int blk = 635;
const int B = 640;

int n,q;
struct heap0{
    int a[B];
    int n;
    int& operator [](int x){return a[x];}
    int top(){return a[1];}
    void pop(){
        int x=1;
        a[1]=a[n--];
        while(x*2<=n){
            int t=x*2;
            if (t<n&&a[t+1]>a[t])t++;
            if (a[t]<=a[x])break;
            swap(a[t],a[x]);
            x=t;
        }
    }
    void insert(int v){
        a[++n]=v;
        int x=n;
        while(x>1&&a[x]>a[x>>1]){
            swap(a[x],a[x>>1]);
            x>>=1;
        }
    }
}a[B];
struct heap1{
    int a[Q];
    int n;
    int& operator [](int x){return a[x];}
    int top(){return a[1];}
    void pop(){
        int x=1;
        a[1]=a[n--];
        while(x*2<=n){
            int t=x*2;
            if (t<n&&a[t+1]<a[t])t++;
            if (a[t]>=a[x])break;
            swap(a[t],a[x]);
            x=t;
        }
    }
    void insert(int v){
        a[++n]=v;
        int x=n;
        while(x>1&&a[x]<a[x>>1]){
            swap(a[x],a[x>>1]);
            x>>=1;
        }
    }
}t[B];
int be[N],rig[N],lef[N];
int val[N];

void rebuild(int x){
    a[x].n=0;
    if (!t[x].n)return;
    fo(i,lef[x],rig[x]){
        int tp=t[x].top();
        if (tp<val[i]){
            t[x].pop();
            t[x].insert(val[i]);
            val[i]=tp;
        }
    }
    t[x].n=0;
}

void putnew(int x){
    a[x].n=0;
    fo(i,lef[x],rig[x])a[x][++a[x].n]=val[i];
    sort(a[x].a+1,a[x].a+a[x].n+1);
    fo(i,1,a[x].n/2)swap(a[x][a[x].n-i+1],a[x][i]);
}

void solve(int l,int r,int &v){
    if (be[l]==be[r]){
        rebuild(be[l]);
        fo(i,l,r)
        if (v<val[i])swap(v,val[i]);
        putnew(be[l]);
        return;
    }
    int lt=be[l],rt=be[r];
    if (l>lef[lt]){
        rebuild(lt);
        for(int i=l;be[i]==lt;i++)
        if (v<val[i])swap(v,val[i]);
        putnew(lt);
        lt++;
    }
    if (r<rig[rt]){
        fo(i,lt,rt-1){
            int tp=a[i].top();
            if (v<tp){
                a[i].pop();
                a[i].insert(v);
                t[i].insert(v);
                v=tp;
            }
        }
        rebuild(rt);
        fo(i,rig[rt-1]+1,r)
        if (v<val[i])swap(v,val[i]);
        putnew(rt);
    }
    else
        fo(i,lt,rt){
            int tp=a[i].top();
            if (v<tp){
                a[i].pop();
                a[i].insert(v);
                t[i].insert(v);
                v=tp;
            }
        }
}

int main(){
    freopen("sushi.in","r",stdin);
    freopen("sushi.out","w",stdout);
    n=get();q=get();
    fo(i,1,n)be[i]=i/blk+1;
    fo(i,1,n)rig[be[i]]=i;
    fd(i,n,1)lef[be[i]]=i;
    fo(i,1,n){
        int x=get();
        val[i]=x;
        a[be[i]].insert(x);
    }
    fo(cas,1,q){
        int l=get(),r=get(),v=get();
        if (l>r){
            solve(l,n,v);
            solve(1,r,v);
        }
        else solve(l,r,v);
        printf("%d\n",v);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值