BZOJ1926 [SDOI2010]粟粟的书架

58 篇文章 0 订阅

Address

Solution

  • 这题实际上可以看做两个问题:

Problem1 对于50%的数据,满足 r,c200,m2×105

  • 问题的一种暴力解决方案显然就是把询问矩阵中的所有书按页数排序,然后不断把页数多的书取出来,直到取出来的总页数 hi
  • 考虑到 pi,j1000 ,我们定义两个数组 val[i][j][k],cnt[i][j][k] 。其中,
    • cnt[i][j][k] 表示左上角为 (1,1) ,右下角为 (i,j) 的矩形中总共有多少本满足 页数 k 的书。
    • val[i][j][k] 表示左上角为 (1,1) ,右下角为 (i,j) 的矩形中满足 页数 k 的书总共有多少页。
  • 这样我们就可以求出 O(1) 求出询问矩阵中大于某个页数的书的数量及总页数。
  • 于是考虑二分,每次二分取出的最小页数(根据暴力的解决方案,大于最小页数的显然都要全部取,我们希望最小页数尽量大,这样取的书也会尽量少),利用 val 数组来判断合法缩小边界,答案用 cnt 数组来计算。
  • 注意和最小页数页数相同的书不一定要全部取,可以直接算出来要取多少个,从而调整答案。
  • 时间复杂度 O(mlog1000+r×c×1000)

Problem2 另有50%的数据,满足 r=1,c5×105,m2×104

  • 因为 r=1 ,这就变成了一个序列问题。
  • 参考前50分的解题过程,我们不难想到用主席树来维护,就能够很方便地求出在询问区间中页数大于等于某个数的书本本数和总页数。
  • 回忆求区间第 K 大数的过程,我们同样可以在权值线段树上二分取出的最小页数,但也要注意相同页数的问题,最后走到叶子节点调整下答案。
  • 时间复杂度 O((c+m)log1000)

Code

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm> 

using namespace std;

namespace INOUT
{
    const int S = 1 << 20;
    char frd[S], *hed = frd + S;
    const char *tal = hed;

    inline char nxtChar()
    {
        if (hed == tal)
            fread(frd, 1, S, stdin), hed = frd;
        return *hed++;
    }

    inline int get()
    {
        char ch; int res = 0; bool flag = false;
        while (!isdigit(ch = nxtChar()) && ch != '-');
        (ch == '-' ? flag = true : res = ch ^ 48);
        while (isdigit(ch = nxtChar()))
            res = res * 10 + ch - 48;
        return flag ? -res : res; 
    }

    inline void put(int x)
    {
        if (x > 9) put(x / 10);
        putchar(x % 10 + 48);
    }
};
using namespace INOUT;

const int N = 205;
int n, m, r, c, lim, p[N][N];
int bx, by, ex, ey, h;

inline void CkMax(int &x, int y) {if (x < y) x = y;}

namespace solve1
{
    const int L = 5e5 + 5;

    int rt[L], p1[L], T;
    struct Chair
    {
        int lc, rc, cnt, val;
        #define l(x) tr[x].lc
        #define r(x) tr[x].rc
        #define c(x) tr[x].cnt
        #define v(x) tr[x].val
    }tr[L * 15];

    inline void Insert(int &x, int y, int l, int r, int vi)
    {
        tr[x = ++T] = tr[y]; ++c(x); v(x) += vi;
        if (l == r) return ;

        int mid = l + r >> 1;
        if (vi <= mid) 
            Insert(l(x), l(y), l, mid, vi);
        else 
            Insert(r(x), r(y), mid + 1, r, vi);
    }

    inline int Query(int x, int y, int l, int r, int k)
    {   
        if (l == r)
        {
            int num = 0;
            while (k > 0) k -= l, ++num;
            return num;
        }

        int mid = l + r >> 1, w = v(r(y)) - v(r(x));
        if (k > w)
            return Query(l(x), l(y), l, mid, k - w) + c(r(y)) - c(r(x));
        else 
            return Query(r(x), r(y), mid + 1, r, k);
    }

    inline void work()
    {
        l(0) = r(0) = c(0) = v(0) = 0;
        for (int i = 1; i <= c; ++i)
            CkMax(lim, p1[i] = get());
        for (int i = 1; i <= c; ++i)
            Insert(rt[i], rt[i - 1], 1, lim, p1[i]);
        while (m--)
        {
            bx = get(); by = get() - 1;
            ex = get(); ey = get(); h = get();

            if (v(rt[ey]) - v(rt[by]) < h) 
                puts("Poor QLW");
            else 
                put(Query(rt[by], rt[ey], 1, lim, h)), putchar('\n'); 
        }
    }
};

namespace solve2
{
    const int M = 1005;
    int val[N][N][M], cnt[N][N][M]; 

    inline int MatrixVal(int mi)
    {
        return val[ex][ey][mi] - val[bx][ey][mi] - val[ex][by][mi] + val[bx][by][mi];
    }

    inline int MatrixCnt(int mi)
    {
        return cnt[ex][ey][mi] - cnt[bx][ey][mi] - cnt[ex][by][mi] + cnt[bx][by][mi];
    }

    inline void work()
    {
        for (int i = 1; i <= r; ++i)
            for (int j = 1; j <= c; ++j)
                CkMax(lim, p[i][j] = get());
        for (int i = 1; i <= r; ++i)
            for (int j = 1; j <= c; ++j)
                for (int k = 1; k <= p[i][j]; ++k)
                    val[i][j][k] += p[i][j], ++cnt[i][j][k];
        for (int i = 1; i <= r; ++i)
            for (int j = 1; j <= c; ++j)
                for (int k = 1; k <= lim; ++k)
                    val[i][j][k] += val[i][j - 1][k], cnt[i][j][k] += cnt[i][j - 1][k];
        for (int j = 1; j <= c; ++j)
            for (int i = 1; i <= r; ++i)
                for (int k = 1; k <= lim; ++k)
                    val[i][j][k] += val[i - 1][j][k], cnt[i][j][k] += cnt[i - 1][j][k];

        while(m--)
        {
            bx = get() - 1; by = get() - 1;
            ex = get(); ey = get(); h = get();

            if (MatrixVal(1) < h) 
                puts("Poor QLW");
            else 
            {
                int l = 1, r = lim, Ans = 0;
                while (l <= r)
                {
                    int mid = l + r >> 1;
                    if (MatrixVal(mid) >= h) 
                        Ans = mid, l = mid + 1;
                    else
                        r = mid - 1;  
                }
                int Sans = MatrixCnt(Ans), Sval = MatrixVal(Ans),
                    num = Sans - MatrixCnt(Ans + 1);
                while (num && Sval - Ans >= h) Sval -= Ans, --num, --Sans;
                put(Sans), putchar('\n');
            }
        }
    }
};

int main()
{
    freopen("susu.in", "r", stdin);
    freopen("susu.out", "w", stdout);

    r = get(); c = get(); m = get();

    if (r == 1) 
        solve1::work();
    else 
        solve2::work();

    fclose(stdin); fclose(stdout);
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值