Luogu P2468 SDOI2010 粟粟的书架 题解 [ 紫 ] [ 可持久化线段树 ] [ 二分 ] [ 前缀和 ]

粟粟的书架:二合一的缝合题。

前一半测试点

此时是 200×200 的二维问题,首先考虑暴力怎么写,显然是每次询问把查的全扔进大根堆里,然后一直取堆顶直到满足要求或者取空。

那么这个暴力的一个显著特征就是我们选了前 k 大的值进去,换句话说,我们就要把大于等于某个值的元素全选进去。于是我们二分这个值,把大于等于这个值的全选上,最后一个满足总和 h 的值就是最后选的元素的值。然后把多出的不需要的元素减掉即可。

具体实现上,我们用二维前缀和记录 (i,j) 处大于等于 k 的元素个数和元素之和,二分 check 一下即可。

时间复杂度 O(nmV+qlogV)

其实这个 trick 是个挺常见的二分 trick 的,在 ABC 里也出过一次。

后一半测试点

此时是 n5×105 的一维问题,我们需要查询一个区间内 h 需要选的最少元素个数。

那么我们根据上文的贪心,显然也是要先选大的,所以我们对每个 1i 建立一个权值线段树,组合起来就成了可持久化线段树,然后向下递归的时候就先选更大的右儿子,右儿子选了不够再选左儿子。注意到叶子结点时需要选的个数为 hcurx,并且修改时是在原基础上增加。

时间复杂度 O(nlogn)

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
int read()
{
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-48,c=getchar();
    return x;
}
int n,m,q,mx=-0x3f3f3f3f;
int ax[205][205],f[205][205][1005],g[205][205][1005];
int getf(int x,int y,int xx,int yy,int k)
{
    return (f[xx][yy][k]-f[xx][y-1][k]-f[x-1][yy][k]+f[x-1][y-1][k]);
}
int getg(int x,int y,int xx,int yy,int k)
{
    return (g[xx][yy][k]-g[xx][y-1][k]-g[x-1][yy][k]+g[x-1][y-1][k]);
}
void solve1()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            ax[i][j]=read();
            mx=max(mx,ax[i][j]);
        }
    }
    for(int k=1;k<=mx;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                f[i][j][k]=(f[i-1][j][k]+f[i][j-1][k]-f[i-1][j-1][k]+(ax[i][j]>=k?ax[i][j]:0));
                g[i][j][k]=(g[i-1][j][k]+g[i][j-1][k]-g[i-1][j-1][k]+(ax[i][j]>=k?1:0));
            }
        }
    }
    while(q--)
    {
        int x,y,xx,yy,h;
        x=read();y=read();xx=read();yy=read();h=read();
        if(getf(x,y,xx,yy,1)<h){printf("Poor QLW\n");continue;}
        int l=1,r=mx,mid;
        while(l<r)
        {
            mid=(l+r+1)>>1;
            if(getf(x,y,xx,yy,mid)>=h)l=mid;
            else r=mid-1;
        }
        printf("%d\n",getg(x,y,xx,yy,l)-(getf(x,y,xx,yy,l)-h)/l);
    }
}
const int N=500005;
int a[N];
struct Node{
    int ls,rs,sm,sz;
};
struct Persegtree{
    Node tr[25*N];
    int root[N],tot=0;
    void pushup(int p)
    {
        tr[p].sm=tr[lc(p)].sm+tr[rc(p)].sm;
        tr[p].sz=tr[lc(p)].sz+tr[rc(p)].sz;
    }
    void update(int &u,int v,int ln,int rn,int x)
    {
        u=++tot;
        tr[u]=tr[v];
        if(ln==rn){tr[u].sm+=x;tr[u].sz+=1;return;}
        int mid=(ln+rn)>>1;
        if(x<=mid)update(lc(u),lc(v),ln,mid,x);
        else update(rc(u),rc(v),mid+1,rn,x);
        pushup(u);
    }
    int query(int u,int v,int ln,int rn,int h)
    {
        if(ln==rn)return int(ceil(1.0*h/ln));
        int mid=(ln+rn)>>1;
        int rsm=tr[rc(u)].sm-tr[rc(v)].sm;
        int rsz=tr[rc(u)].sz-tr[rc(v)].sz;
        if(rsm<h)return (query(lc(u),lc(v),ln,mid,h-rsm)+rsz);
        else return query(rc(u),rc(v),mid+1,rn,h);
    }
}tr1;
void solve2()
{
    for(int i=1;i<=m;i++)
    {
        a[i]=read();
        mx=max(mx,a[i]);
    }
    for(int i=1;i<=m;i++)tr1.update(tr1.root[i],tr1.root[i-1],1,mx,a[i]);
    while(q--)
    {
        int tmp,l,r,h;
        tmp=read();l=read()-1;tmp=read();r=read();h=read();
        if(tr1.tr[tr1.root[r]].sm-tr1.tr[tr1.root[l]].sm<h){printf("Poor QLW\n");continue;}
        printf("%d\n",tr1.query(tr1.root[r],tr1.root[l],1,mx,h));
    }
}
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    n=read();m=read();q=read();
    if(n==1)solve2();
    else solve1();
    return 0;
}
posted @   KS_Fszha  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示