P5356 [Ynoi2017] 由乃打扑克 题解

纪念一下人生第一道 Ynoi 题目链接

题意

是个人都看得懂吧。。。。。。

solution

看到 Ynoi,想到什么?

  • 分块
  • 卡常

你猜对了,这题又是分块。

数列分块入门 2中我们已经学会了询问区间内小于某个值 $x$ 的元素个数。

很容易想到二分元素的值域,如果小于等于 $x$ 的小于 $k$,那么就往大枚举,否则往下枚举,更新答案。

code:

while(l <= r){
    int mid = (l+r)>>1;
    int check = query(dl, dr, mid);
    if(check < k)   l = mid+1;
    else r = mid-1, ans = mid;
}

看着非常地正确。但时间复杂度是 $O(m \sqrt{n} \log{n} \log{v})$

很遗憾,被我卡了 -lxl

优化

但是作为最简单的Ynoi,它是可以被以上暴力c过去的。

加上一下优化就可以过了:

  • 二分边界取区间最大和最小值
  • 查询时,如果当前区间的最大值小于 $k$ 那么就可以直接加上区间大小,如果当前区间最小值大于 $k$ 那么就不用再去计算它了。
  • 玄学的块长

代码

#include<cmath>
#include<cstdio>
#include<algorithm>
const int MAX = 100005;
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define rg register

static char buf[1000000],*p1=buf,*p2=buf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
inline int read(){int x=0,f=1;char c=getchar();while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+c-48;c=getchar();}return x*f;}
inline void write(int x){static char buf[20];static int len=-1;if(x<0)putchar('-'),x=-x;do buf[++len]=x%10,x/=10;while(x);while(len>=0)putchar(buf[len--]+48);}

int a[MAX];
int b[MAX];
int id[MAX]; 
int pls[1005];

int L[1005], R[1005];

inline void add(int l, int r, int c){
    int p = id[l], q = id[r];
    if(p == q){
        for(rg int i = l; i <= r; i++)  b[i] += c;
        for(rg int i = L[p]; i <= R[p]; i++){
            a[i] = b[i];
        }
        std::sort(a+L[p], a+R[p]+1);
    }else{
        for(rg int i = l; i <= R[p]; ++i)   b[i] += c;
        for(rg int i = L[p]; i <= R[p]; ++i)    a[i] = b[i];
        std::sort(a+L[p], a+R[p]+1);
        for(rg int i = p+1; i <= q-1; ++i)  pls[i] += c;
        for(rg int i = r; i >= L[q]; --i)   b[i] += c;
        for(rg int i = L[q]; i <= R[q]; ++i)    a[i] = b[i];
        std::sort(a+L[q], a+R[q]+1);
    }
}

inline int query(int l, int r, int c){
    int p = id[l], q = id[r];
    int ans = 0;
    if(p == q){
        if(a[R[p]] + pls[p] <= c)   ans += r-l+1;
        else if(a[L[p]] + pls[p] <= c)  for(int i = l; i <= r; ++i) ans+=b[i] + pls[p] <= c;
    }else{
        if(a[R[p]] + pls[p] <= c)   ans += R[p]-l+1;
        else if(a[L[p]] + pls[p] <= c)  for(int i = l; i <= R[p]; i++)  ans+=b[i] + pls[p] <= c;
        int u,v,mid;
        for(rg int i = p+1; i <= q-1; ++i){
            if(a[L[i]]+pls[i]>c)continue;
            if(a[R[i]]+pls[i]<=c){
                ans+=R[i]-L[i]+1;
                continue;
            }
            u=L[i],v=R[i],mid;
            while(u<v){
                int mid=u+((v-u)>>1)+1;
                if(a[mid]+pls[i]<=c)u=mid;
                else v=mid-1;
            }
            ans+=u-(i-1)*(R[i]-L[i]+1);
        }
        if(a[R[q]] + pls[q] <= c)   ans += r - L[q] + 1;
        else if(a[L[q]] + pls[q] <= c)  for(int i = L[q]; i <= r; i++)  ans+=b[i] + pls[q] <= c;
    }
    return ans;
}

inline int getmax(int l, int r){
    int p = id[l], q = id[r];
    int ans = -0x3f3f3f3f;
    if(p == q){
        for(rg int i = l; i <= r; i++)  ans = max(ans, b[i]+pls[p]);
    }else{
        if(a[R[p]]+pls[p]>ans)for(rg int i = l; i <= R[p]; i++) ans = max(ans, b[i]+pls[p]);
        for(rg int i = p+1; i <= q-1; i++)  ans = max(ans, a[R[i]]+pls[i]);
        if(a[R[q]]+pls[q]>ans)for(rg int i = L[q]; i <= r; i++) ans = max(ans, b[i]+pls[q]);
    }
    return ans;
}

inline int getmin(int l, int r){
    int p = id[l], q = id[r];
    int ans = 0x3f3f3f3f;
    if(p == q){
        for(rg int i = l; i <= r; i++)  ans = min(ans, b[i]+pls[p]);
    }else{
        if(a[L[p]]+pls[p]<ans)for(rg int i = l; i <= R[p]; ++i) ans = min(ans, b[i]+pls[p]);
        for(rg int i = p+1; i <= q-1; ++i)  ans = min(ans, a[L[i]]+pls[i]);
        if(a[L[q]]+pls[q]<ans)for(rg int i = L[q]; i <= r; ++i) ans = min(ans, b[i]+pls[q]);
    }
    return ans;
}
int c[2007];
inline int ask(int dl, int dr, int k){
    if(dr-dl+1 < k or k < 1)    return -1;
    int l = getmin(dl, dr), r = getmax(dl, dr);
    if(id[dl] == id[dr]){
        for(int i = 0; i <= dr - dl; ++i){
            c[i] = b[i+dl];
        }
        std::sort(c, c+dr-dl+1);
        return c[k-1]+pls[id[dl]];
    }
    int ans = 0;
    while(l <= r){
        int mid = l+((r-l)>>1);
        int check = query(dl, dr, mid);
        if(check < k)   l = mid+1;
        else r = mid-1, ans = mid;
    }
    return ans;
}

signed main(){
    int n = read(), m = read();
    for(rg int i = 1; i <= n; ++i)  a[i] = read(), b[i] = a[i];
    int t(200);
    for(int i=1;i<=n;i++)id[i]=(i-1)/t+1;
    for(int i=1;i<=id[n];i++)L[i]=(i-1)*t+1,R[i]=i*t;
    R[id[n]]=n;
    for(rg int i = 1; i <= id[n]; ++i){
        std::sort(a+L[i], a+R[i]+1);
    }
    int op,l,r,k;
    while(m--){
        op = read(), l = read(), r = read(), k = read();
        if(op-1) add(l, r, k);
        else write(ask(l, r, k)), putchar('\n');
    }
    return 0;
} 

顺利卡入最优解第一页。

posted @ 2022-09-24 15:19  WRuperD  阅读(21)  评论(0编辑  收藏  举报  来源

本文作者:DIVMonster

本文链接:https://www.cnblogs.com/guangzan/p/12886111.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

这是一条自定义内容

这是一条自定义内容