其他分享
首页 > 其他分享> > P2468 [SDOI2010] 粟粟的书架

P2468 [SDOI2010] 粟粟的书架

作者:互联网

1 P2468 [SDOI2010] 粟粟的书架

2 题目描述

时间限制 \(3s\) | 空间限制 \(500M\)

幸福幼儿园 \(B29\) 班的粟粟是一个聪明机灵、乖巧可爱的小朋友,她的爱好是画画和读书,尤其喜欢 \(Thomas H. Cormen\) 的文章。粟粟家中有一个 \(R\) 行 \(C\) 列的巨型书架,书架的每一个位置都摆有一本书,上数第 \(i\) 行、左数第 \(j\) 列摆放的书有 \(P_{i,j}\) 页厚。

粟粟每天除了读书之外,还有一件必不可少的工作就是摘苹果,她每天必须摘取一个指定的苹果。粟粟家果树上的苹果有的高、有的低,但无论如何凭粟粟自己的个头都难以摘到。不过她发现,如果在脚下放上几本书,就可以够着苹果;她同时注意到,对于第 \(i\) 天指定的那个苹果,只要她脚下放置书的总页数之和不低于 \(Hi\),就一定能够摘到。

由于书架内的书过多,父母担心粟粟一天内就把所有书看完而耽误了上幼儿园,于是每天只允许粟粟在一个特定区域内拿书。这个区域是一个矩形,第 \(i\) 天给定区域的左上角是上数第 \(x_{1,i}\) 行的左数第 \(y_{1,i}\) 本书,右下角是上数第 \(x_{2,i}\) 行的左数第 \(y_{2,i}\) 本书。换句话说,粟粟在这一天,只能在这 \(﹙x_{2,i}-x_{1,i+1}﹚×﹙y_{2,i}-y_{1,i+1}﹚\) 本书中挑选若干本垫在脚下,摘取苹果。

粟粟每次取书时都能及时放回原位,并且她的书架不会再撤下书目或换上新书,摘苹果的任务会一直持续M天。给出每本书籍的页数和每天的区域限制及采摘要求,请你告诉粟粟,她每天至少拿取多少本书,就可以摘到当天指定的苹果。

数据范围:

3 题解

简化一下题意:给出一个 \(r \times c\) 的矩形和 \(m\) 次询问。每次询问一个子矩形中满足和大于等于 \(h\) 的最少的数的个数。

我们先利用一个简单的贪心转化一下问题:我们选择的数一定是这个子矩阵中最大的几个数。这是因为这几个数的和一定是最大的,所有满足大于等于 \(h\) 的可能性最大。这样,问题就变成了求子矩阵中前多少大的数之和大于等于 \(h\)。看到这里的前多少大,我们就可以想到二分。但是,查询前几大的数不太好做,所以我们改为大于等于 \(k\) 的数的个数。

这个问题第一眼看上去比较不可做,甚至可能要用线段树套主席树。但是我们仔细阅读一下题面,发现:

对于 \(50\%\) 的数据,满足 \(r, c \le 200, m \le 2 \times 10^5\)。

另有 \(50\%\) 的数据,满足 \(r = 1, c \le 5 \times 10^5, m \le 2 \times 10^4\)。

也就是说,要么是一个序列上的问题,要么是一个数据范围极小的问题。

我们先啃硬骨头,来看看这个序列上的问题。

注意到大于等于某个数的个数这个值一定满足区间可加可减性,所以我们可以用主席树来维护这个答案。具体地,每颗动态开点的权值线段树维护区间某一值域中数的个数与数的值之和。询问时先二分 \(k\),在得到 \(k\) 后直接查询在值域 \([k, 1000]\) 中所有数的值的和是否大于等于 \(h\)。如果不是,那么继续二分,否则我们将当前的 \(k\) 记录下来。

这个时候我们看起来可以在二分后直接输出 \([ans, 1000]\) 中所有数的个数,但是我们发现有一个问题:对于权值 \(ans\) 来说,我们如果有超过一个 \(ans\),那么有可能一部分 \(ans\) 不加仍然大于等于 \(h\),此时这部分 \(ans\) 就应当减去,因此答案可能会变多。这个问题的解决方法比较简单:算出 \([ans, 1000]\) 中所有数的值的和与 \(h\) 的差, 将这个差除以 \(ans\),此时算出的数就是减去仍然可行的 \(ans\) 个数,输出时直接减去这一部分即可。时间复杂度为 \(O(n \log^2n)\)。

我们再来看稍微容易一点的 \(r, c \le 200\)。

这里我们考虑用二维前缀和维护子矩阵的大于等于 \(k\) 的数的值之和与数的个数。总共需要 \(O(1000rc)\) 的时间来预处理。随后的处理方法类似于我们序列上的处理方法,再套一个二分即可。

时间复杂度为 \(O(1000rc + n \log n)\)。可以通过本题。

4 代码(空格警告):

#include <iostream>
#include <cstdio>
using namespace std;
const int P1 = 5e5+2, P2 = 202, P2M = 1002;
int R, c, m;
int tot;
int rt[P1];
int Map[P2][P2], sum[P2][P2][P2M], cnt[P2][P2][P2M];
struct node
{
    int lc, rc, sum, cnt;
}t[P1 * 20];
int Sum(int x, int y, int xx, int yy, int k) {return sum[xx][yy][k] - sum[x - 1][yy][k] - sum[xx][y - 1][k] + sum[x - 1][y - 1][k];}
int Cnt(int x, int y, int xx, int yy, int k) {return cnt[xx][yy][k] - cnt[x - 1][yy][k] - cnt[xx][y - 1][k] + cnt[x - 1][y - 1][k];}
int build()
{
    tot++;
    t[tot].lc = t[tot].rc = t[tot].sum = 0;
    return tot;
}
void pushup(int p)
{
    t[p].sum = t[t[p].lc].sum + t[t[p].rc].sum;
    t[p].cnt = t[t[p].lc].cnt + t[t[p].rc].cnt;
}
void modify(int p, int p2, int l, int r, int pos)
{
    if (l == r)
    {
        t[p].sum = t[p2].sum + pos;
        t[p].cnt = t[p2].cnt + 1;
        return ;
    }
    int mid = (l + r) / 2;
    if (pos <= mid)
    {
        if (!t[p].lc) t[p].lc = build();
        modify(t[p].lc, t[p2].lc, l, mid, pos);
        t[p].rc = t[p2].rc;
    }
    else
    {
        if (!t[p].rc) t[p].rc = build();
        modify(t[p].rc, t[p2].rc, mid+1, r, pos);
        t[p].lc = t[p2].lc;
    }
    pushup(p);
}
node query(int p, int l, int r, int L, int R)
{
    node ans;
    ans.sum = ans.cnt = 0;
    if (!p) return ans;
    if (l >= L && r <= R) return t[p];
    int mid = (l + r) / 2, cnt = (L <= mid) + (r > mid);
    if (cnt == 1)
    {
        if (L <= mid) return query(t[p].lc, l, mid, L, R);
        return query(t[p].rc, mid+1, r, L, R);
    }
    node a, b;
    a = query(t[p].lc, l, mid, L, R);
    b = query(t[p].rc, mid+1, r, L, R);
    ans.sum = a.sum + b.sum;
    ans.cnt = a.cnt + b.cnt;
    return ans;
}
signed main()
{
    scanf("%d %d %d", &R, &c, &m);
    if (R == 1)
    {
        int p[P1], x, y, h, l, r, mid, ans, ans1, cnt1;
        for (int i = 1; i <= c; i++) scanf("%d", &p[i]);
        rt[0] = build();
        for (int i = 1; i <= c; i++)
        {
            rt[i] = build();
            modify(rt[i], rt[i-1], 1, 1000, p[i]);
        }
        for (int i = 1; i <= m; i++)
        {
            ans = 0;
            scanf("%d %d", &x, &x);
            scanf("%d %d", &y, &y);
            scanf("%d", &h);
            l = 1;
            r = 1000;
            while (l <= r)
            {
                mid = (l + r) / 2;
                if (query(rt[y], 1, 1000, mid, 1000).sum - query(rt[x - 1], 1, 1000, mid, 1000).sum >= h) ans = mid, l = mid + 1;
                else r = mid - 1;
            }
            if (!ans)
            {
                printf("Poor QLW\n");
                continue;
            }
            ans1 = query(rt[y], 1, 1000, ans, 1000).cnt - query(rt[x - 1], 1, 1000, ans, 1000).cnt;
            cnt1 = ((query(rt[y], 1, 1000, ans, 1000).sum - query(rt[x - 1], 1, 1000, ans, 1000).sum) - h) / ans;
            ans1 -= cnt1;
            printf("%d\n", ans1);
        }
    }
    else
    {
        int x, y, xx, yy, l, r, mid, h, ans, ans1, cnt1;
        for (int i = 1; i <= R; i++)
        {
            for (int j = 1; j <= c; j++) scanf("%d", &Map[i][j]);
        }
        for (int i = 1; i <= R; i++)
        {
            for (int j = 1; j <= c; j++)
            {
                for (int k = 1; k <= 1000; k++)
                {
                    sum[i][j][k] = sum[i-1][j][k] + sum[i][j-1][k] - sum[i-1][j-1][k];
                    cnt[i][j][k] = cnt[i-1][j][k] + cnt[i][j-1][k] - cnt[i-1][j-1][k];
                    if (Map[i][j] >= k) sum[i][j][k] += Map[i][j], cnt[i][j][k]++;
                }
            }
        }
        for (int i = 1; i <= m; i++)
        {
            ans = 0;
            scanf("%d %d %d %d %d", &x, &y, &xx, &yy, &h);
            l = 1;
            r = 1000;
            while (l <= r)
            {
                int mid = (l + r) / 2;
                if (Sum(x, y, xx, yy, mid) >= h) ans = mid, l = mid + 1;
                else r = mid - 1;
            }
            if (!ans)
            {
                printf("Poor QLW\n");
                continue;
            }
            ans1 = Cnt(x, y, xx, yy, ans);
            cnt1 = (Sum(x, y, xx, yy, ans) - h) / ans;
            ans1 -= cnt1;
            printf("%d\n", ans1);
        }
    }
    return 0;
}

欢迎关注我的微信公众号:智子笔记

标签:粟粟,cnt,int,sum,SDOI2010,xx,ans,P2468,1000
来源: https://www.cnblogs.com/david24/p/14787170.html