1926: [Sdoi2010]粟粟的书架|主席树|二分答案

ym_fqk神犇提供做法

一边看AlphaGo VS 李世石一边写这个sb题居然1A辣。
做法显然。n=1时就是序列上主席树然后二分答案,否则就是区间维护1000个前缀和暴力再二分答案。最后要暴力一下最小值的出现次数。

暴力出奇迹,内存贴线过,差点被卡飞。。

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int sc()
{
    int i=0; char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();
    return i;
}
int sum[1005][205][205],s[1005][205][205];
int a[205][205],b[500005],root[500005];
int t[8000008],sm[8000008],ch[8000008][2];
int n,m,Q,cnt;
int SUM(int d,int x1,int y1,int x2,int y2)
{
    return sum[d][x2][y2]-sum[d][x1-1][y2]-sum[d][x2][y1-1]+sum[d][x1-1][y1-1];
}
void insert(int pre,int &x,int l,int r,int v)
{
    if(!x)x=++cnt;
    sm[x]=sm[pre]+v;t[x]=t[pre]+1;
    if(l==r)return;
    int mid=l+r>>1;
    if(v<=mid)
        ch[x][1]=ch[pre][1],
        insert(ch[pre][0],ch[x][0],l,mid,v);
    else 
        ch[x][0]=ch[pre][0],
        insert(ch[pre][1],ch[x][1],mid+1,r,v);
}
int query(int L,int R,int H)
{
    if(sm[R]-sm[L]<H)return 0;
    int l=1,r=1000,tot=0;
    while(l!=r)
    {
        int mid=l+r>>1;
        int re=t[ch[R][1]]-t[ch[L][1]];
        int res=sm[ch[R][1]]-sm[ch[L][1]];
        if(res>=H)
            L=ch[L][1],R=ch[R][1],l=mid+1;
        else L=ch[L][0],R=ch[R][0],r=mid,H-=res,tot+=re;
    }
    tot+=t[R]-t[L];
    int ans=sm[R]-sm[L];
    while(ans-l>=H)ans-=l,tot--;
    return tot;
}
void solve1()
{
    for(int i=1;i<=m;i++)
        b[i]=sc(),insert(root[i-1],root[i],1,1000,b[i]);
    while(Q--)
    {
        int x1=sc(),l=sc(),x2=sc(),r=sc(),H=sc();
        int ans=query(root[l-1],root[r],H);
        if(!ans) puts("Poor QLW");
        else printf("%d\n",ans);
    }
}
void solve2()
{
    int mx=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            mx=max(mx,a[i][j]=sc());
    for(int i=1;i<=mx;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=m;k++)
            {
                sum[i][j][k]=sum[i][j-1][k]+sum[i][j][k-1]-sum[i][j-1][k-1];
                s[i][j][k]=s[i][j-1][k]+s[i][j][k-1]-s[i][j-1][k-1];
                if(a[j][k]==i)sum[i][j][k]+=i,s[i][j][k]++;
            }
    for(int i=mx-1;i;i--)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=m;k++)
                s[i][j][k]+=s[i+1][j][k],
                sum[i][j][k]+=sum[i+1][j][k];
    while(Q--)
    {
        int x1=sc(),y1=sc(),x2=sc(),y2=sc(),H=sc();
        int l=1,r=mx,ans=0;
        while(l<=r)
        {
            int mid=l+r>>1;
            int res=SUM(mid,x1,y1,x2,y2);
            if(res>=H)ans=mid,l=mid+1;else r=mid-1;
        }
        if(ans)
        {
            int res=SUM(ans,x1,y1,x2,y2);
            int tot=s[ans][x2][y2]-s[ans][x1-1][y2]-s[ans][x2][y1-1]+s[ans][x1-1][y1-1];
            while(res-ans>=H)res-=ans,tot--;
            printf("%d\n",tot);
        }else puts("Poor QLW");
    }
}
int main()
{
    n=sc(),m=sc(),Q=sc();
    if(n==1)solve1();else solve2();
    return 0;
}

你可能感兴趣的:(主席树)