P5356 [Ynoi Easy Round 2017] 由乃打扑克 题解?

前言

  • 做法大概率是乱搞的,狠数据应该可以轻松卡掉,主要是为了记一下动态区间第 \(K\) 小怎么做
  • msjing差点发明出来了

\(solve\)

  • 首先不带修的就是主席树,单推就是树套树,这题的多修就不会了
  • 所以我们尝试发明一下
  • 如果就不带修查一次,直接排序可以 \(O(1)\)
  • 对于一个区间的修,我们修完后再排序还可以 \(O(1)\) 找,但是复杂度巨劣
  • 发现排序复杂度太高了,由于你并不能减少排序次数,只能减少排序长度,所以我们在排序长度上下手
  • 欸,分块
  • 哒哒哒哒哒,好想写分块
  • 我们直接让排序区间变成根号长,复杂度就降下来了
  • 那就好说了
  • 欸我好像是这么发明的吧
  • 我们对于每个块局部排序(这样如果修区间包括这个块就不用重新排了,也方便后面操作),记录原下标(方便修)
  • 你可以维护两个数组,一个原数组,一个排序数组,方便维护整块散块
  • 对于修的操作,块内好说,直接改
  • 查询包括多个块,先将散块直接暴力改,改完直接对所在块排序,注意修的时候要分清原数组还是排序数组,排序数组需要按原来的下标对应修,整块直接打 \(tag\),像线段树一样
  • 查询非常麻烦
  • 如果查询在块内好说,但是包括一堆块就不好办了,我们可以方便处理每一个块内的所有排名,但是多个完全不好做
  • msjing就发明到这了
  • 等一下,每个块内都是排好序的,即可以直接二分
  • 那我们先二分第 \(K\) 小的数是多少,散块暴力统计有几个数小于等于二分值,整块直接二分找,如果总共小于 \(K\),小了,将 \(L\) 向右推,否则将 \(R\) 向左推,并更新答案,由于这个常数巨大,所以我们要加个剪枝:如果一个块内最小的都比二分值大,不可能产生贡献,跳过;如果块内最大的都比二分值小,整块都可以做贡献,全加上,跳过
  • 注意你的外层二分范围,别开小了,要不然你就现求范围
  • 无解是 \(K\) 比区间长还大,或者查询比 \(1\)
  • 查询二分部分参考尺子大神的题解因为msjing二分写炸了
  • 其他就没啥了,注意一下是用原序列还是排序序列,记得加上 \(tag\) 的值
  • 对了,题的样例跟滚木一样,msjing手搓了俩
样例
  • 1.in
5 3
1 5 8 2 6
1 2 3 1
2 1 3 3
1 2 4 2

  • 1.out
5
8
  • 2.in
10 5
1 1 4 5 1 4 9 1 7 8
1 2 8 2
2 1 5 2
1 2 8 2
2 3 10 1
1 5 10 3

  • 2.out
1
3
5

\(code\)

  • 复杂度大概是 \(O(n \sqrt n \log ^2 n)\) 的,其实挺劣
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
#define fi first
#define se second
using namespace std;
constexpr int maxn=1e6+10;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch == '-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
int n,m;
int a[maxn];
int L[maxn],R[maxn],pos[maxn],cnt;
pii b[maxn];
void init()
{
    cnt=sqrt(n);
    for (int i=1;i<=cnt;i++)
        L[i]=R[i-1]+1,R[i]=cnt*i;
    if (R[cnt]<n)
        cnt++,L[cnt]=R[cnt-1]+1,R[cnt]=n;
    for (int i=1;i<=cnt;i++)
        for (int j=L[i];j<=R[i];j++)
            pos[j]=i;
}
int tag[maxn];
void Upd(int l,int r,int v)
{
    if (pos[l] == pos[r])
    {
        for (int i=L[pos[l]];i<=R[pos[r]];i++)
            if (b[i].se>=l && b[i].se<=r)
                b[i].fi+=v;
        for (int i=l;i<=r;i++) a[i]+=v;
        sort(b+L[pos[l]],b+R[pos[r]]+1);
        return;
    }
    for (int i=pos[l]+1;i<=pos[r]-1;i++)
        tag[i]+=v;
    for (int i=L[pos[l]];i<=R[pos[l]];i++)
        if (b[i].se>=l && b[i].se<=R[pos[l]])
            b[i].fi+=v;
    for (int i=l;i<=R[pos[l]];i++) a[i]+=v;
    sort(b+L[pos[l]],b+R[pos[l]]+1);
    for (int i=L[pos[r]];i<=R[pos[r]];i++)
        if (b[i].se>=L[pos[r]] && b[i].se<=r)
            b[i].fi+=v;
    for (int i=L[pos[r]];i<=r;i++) a[i]+=v;
    sort(b+L[pos[r]],b+R[pos[r]]+1);
}
int chk(int l,int r,int x,int k)
{
    int sum=0;
    if (pos[l] == pos[r])
    {
        for (int i=l;i<=r;i++)
            if (a[i]+tag[pos[l]]<=x)
                sum++;
        if (sum<k) return 1;
        else return 0;
    }
    for (int i=l;i<=R[pos[l]];i++)
        if (a[i]+tag[pos[l]]<=x)
            sum++;
    for (int i=L[pos[r]];i<=r;i++)
        if (a[i]+tag[pos[r]]<=x)
            sum++;
    for (int i=pos[l]+1;i<=pos[r]-1;i++)
    {
        int mn=L[i],mx=R[i];
        if (b[mn].fi+tag[i]>x) continue;
        if (b[mx].fi+tag[i]<=x) {sum+=R[i]-L[i]+1;continue;}
        while (mn<mx)
        {
            int mid=(mn+mx)>>1;
            mid++;
            if (b[mid].fi+tag[i]<=x) mn=mid;
            else mx=mid-1;
        }
        if (b[mn].fi+tag[i]<=x) sum+=mn-L[i]+1;
    }
    if (sum<k) return 1;
    else return 0;
}
int c[maxn],tot;
int que(int l,int r,int k)
{
    if (r-l+1<k || k<1) return -1;
    int L=-2e9,R=2e9,ans=0;
    while (L<=R)
    {
        int mid=(L+R) >> 1;
        if (chk(l,r,mid,k)) L=mid+1;
        else R=mid-1,ans=mid;
    }
    return ans;
}
signed main()
{
    n=read(),m=read();
    for (int i=1;i<=n;i++) a[i]=read(),b[i]={a[i],i};
    init();
    for (int i=1;i<=cnt;i++)
        sort(b+L[i],b+R[i]+1);
    while (m--)
    {
        int op=read(),l=read(),r=read(),k=read();
        if (op == 2) Upd(l,r,k);
        else printf("%lld\n",que(l,r,k));
    }
    return 0;
}

后话

  • 啊啊啊啊啊差点发明了我为啥忘了二分值啊
posted @ 2026-08-23 18:58  msjing  阅读(13)  评论(0)    收藏  举报