BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案)

  • 题意 :

给你一个长为\(R\)宽为\(C\)的矩阵,第\(i\)行\(j\)列的数为\(P_{i,j}\).

有\(m\)次询问,每次有5个参数\(x_l,x_r,y_l,y_r,h\). 求以\((x_l,y_l)\)为左上角和\((x_r,y_r)\)为右下角的矩形中,至少要选几个值,使得它们的和\(\geq h\).

  • 数据范围 :

对于\(50 \%\)的数据,满足\(R,C \le 200,M \le 200,000\)

另有\(50 \%\)的数据,满足\(R=1,C \le 500,000,M \le 20,000\)

对于\(100 \%\)的数据,满足\(1 \le P_{i,j} \le 1,000, 1 \le h \le 2,000,000,000\)

  • 题解 :

这道题很有意思2333

这个题相当于二合一吧,两种不同的方法解决它的子问题(但殊途同归).

  • 第一个\(R,C \le 200,M \le 200,000\).

    这个是有点类似于暴力的做法,首先预处理出每种数字出现次数的关于位置和数字大小前缀和,以及出现数字和关于位置数字大小的前缀和.

    这个有点绕,直接举例子吧.比如我程序中的Sum[i][j][k]Tot[i][j][k].

    Sum[i][j][k]就是以\((1,1)\)为左上角\((i,j)\)为右下角矩形的,数字\(\ge k\)的和.

    Tot[i][j][k]就是以\((1,1)\)为左上角\((i,j)\)为右下角矩形的,数字\(\ge k\)的出现次数的和.

    然后每次就可以二分你需要选的最小的数字了,每次判断可行就直接前缀和容斥就行了.

    然后这个选的最小数字不一定要选满,要最后算一下这个数字要选多少个.

    令\(n=max(P_{i,j})\)时间复杂度\(O(nRC+m \log n)\),空间复杂度\(O(nRC)\).

  • 第二个\(R=1,C \le 500,000,M \le 20,000\).

    这个就是一个序列操作了,这个我认为是这道题的精髓.

    我们沿用前一个算法的思想,也是要处理序列上那两个东西.

    但时间复杂度肯定要进行优化. 所以有一个数据结构可以支持这个操作,就是主席树!

    主席树不仅支持查找区间第\(k\)大,而且还能支持查找区间在\([l,r]\)中数字的和 和 出现数字的和!(看来我数据结构学的真的蠢啊) 以后出现这种题要往这上面想想了.

    然后我们可以每次算答案的时候直接在主席树上二分就行了和刚才的操作差不多吧,也类似于查找第\(k\)大.

    时间复杂度\(O(C \log n + m \log n)\),空间复杂度\(O(C \log n)\).

  • 代码 :

/**************************************************************
    Problem: 1926
    User: zjp_shadow
    Language: C++
    Result: Accepted
    Time:4688 ms
    Memory:509916 kb
****************************************************************/

#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i)
#define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;

bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}

inline int read() {
    int x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0');
    return x * fh;
}

void File() {
#ifdef zjp_shadow
    freopen ("P1926.in", "r", stdin);
    freopen ("P1926.out", "w", stdout);
#endif
}

const int N = 5e5 + 1e3;
const int maxnode = N * 20;

int take;

inline int Updiv(int a, int b) { return (a / b) + (a % b ? 1 : 0); }

struct ChairMan_Tree {
    int T[N], sumv[maxnode], tot[maxnode], lc[maxnode], rc[maxnode], Size;
    ChairMan_Tree () { Size = 0; }

    void Update(int &o, int pre, int l, int r, int up) {
        o = ++Size; lc[o] = lc[pre]; rc[o] = rc[pre];
        sumv[o] = sumv[pre] + up; tot[o] = tot[pre] + 1;
        if (l == r) return ;

        int mid = (l + r) >> 1;
        if (up <= mid) Update(lc[o], lc[pre], l, mid, up);
        else Update(rc[o], rc[pre], mid + 1, r, up);
    }

    void Query(int s, int t, int l, int r, int val) {
        if (l == r) { take += Updiv(val, l); return ; }
        int here = tot[rc[t]] - tot[rc[s]], sv = sumv[rc[t]] - sumv[rc[s]], mid = (l + r) >> 1;
        if (val > sv) { take += here; Query(lc[s], lc[t], l, mid, val - sv); }
        else Query(rc[s], rc[t], mid + 1, r, val);
    }
} CT;

int r, c, m;
int Mat[210][210];
int Sum[210][210][1010];
int Tot[210][210][1010];

int sum[N];

void Solve2() {
    For (i, 1, c) {
        int val = read();
        sum[i] = sum[i - 1] + val;
        CT.Update(CT.T[i], CT.T[i - 1], 1, 1000, val);
    }
    For (i, 1, m) {
        read(); int l = read(); read(); int r = read(), h = read();
    //  cout << "Ask: " << l << ' ' << r << ' ' << h << endl;
        take = 0;
        if (sum[r] - sum[l - 1] < h) { printf ("Poor QLW\n"); continue ; }
        CT.Query(CT.T[l - 1], CT.T[r], 1, 1000, h);
        printf ("%d\n", take);
    }
}

inline int Calc_Sum (int xl, int yl, int xr, int yr, int low) {
    return Sum[xr][yr][low] - Sum[xl - 1][yr][low] - Sum[xr][yl - 1][low] + Sum[xl - 1][yl - 1][low];
}

inline int Calc_Tot (int xl, int yl, int xr, int yr, int low) {
    return Tot[xr][yr][low] - Tot[xl - 1][yr][low] - Tot[xr][yl - 1][low] + Tot[xl - 1][yl - 1][low];
}

void Solve1() {
    For (i, 1, r)
        For (j, 1, c) {
            Mat[i][j] = read(); Sum[i][j][Mat[i][j]] += Mat[i][j]; ++Tot[i][j][Mat[i][j]];
            For (k, 1, 1000) {
                Sum[i][j][k] += Sum[i][j - 1][k] + Sum[i - 1][j][k] - Sum[i - 1][j - 1][k];
                Tot[i][j][k] += Tot[i][j - 1][k] + Tot[i - 1][j][k] - Tot[i - 1][j - 1][k];
            }
        }

    For (i, 1, r)
        For (j, 1, c)
            Fordown (k, 1000, 1) {
                Sum[i][j][k] += Sum[i][j][k + 1];
                Tot[i][j][k] += Tot[i][j][k + 1];
            }

    For (i, 1, m) {
        int xi = read(), yi = read(), xj = read(), yj = read(), h = read();
        int l = 1, r = 1000, need = -1;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (Calc_Sum(xi, yi, xj, yj, mid) >= h) need = mid, l = mid + 1;
            else r = mid - 1;
        }
        if (need == -1) { printf ("Poor QLW\n"); continue ; }
        int Btot = Calc_Tot(xi, yi, xj, yj, need + 1),
            Bsum = Calc_Sum(xi, yi, xj, yj, need + 1);
        Btot += Updiv(h - Bsum, need);
        printf ("%d\n", Btot);
    }
}

int main () {
    File();
    r = read(); c = read(); m = read();
    if (r == 1) Solve2(); else Solve1();
    return 0;
}

BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案)的更多相关文章

  1. bzoj 1926: [Sdoi2010]粟粟的书架

    #include<cstdio> #include<iostream> #define N 201 #define M 500008 using namespace std; ...

  2. BZOJ 3514: Codechef MARCH14 GERALD07加强版 [LCT 主席树 kruskal]

    3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1312  Solved: 501 ...

  3. 【BZOJ 3626】 [LNOI2014]LCA【在线+主席树+树剖】

    题目链接: TP 题解:   可能是我比较纱布,看不懂题解,只好自己想了…… 先附一个离线版本题解[Ivan] 我们考虑对于询问区间是可以差分的,然而这并没有什么卵用,然后考虑怎么统计答案. 首先LC ...

  4. 13年山东省赛 Boring Counting(离线树状数组or主席树+二分or划分树+二分)

    转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud 2224: Boring Counting Time Limit: 3 Sec   ...

  5. bzoj 2733: [HNOI2012]永无乡 离线+主席树

    2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1167  Solved: 607[Submit][Status ...

  6. BZOJ 3514: Codechef MARCH14 GERALD07加强版( LCT + 主席树 )

    从左到右加边, 假如+的边e形成环, 那么记下这个环上最早加入的边_e, 当且仅当询问区间的左端点> _e加入的时间, e对答案有贡献(脑补一下). 然后一开始是N个连通块, 假如有x条边有贡献 ...

  7. 【BZOJ2653】【主席树+二分】middle

    Description 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整. 给你一个长度为n的序列s. 回答Q个这样的询问:s的左端点在[a,b ...

  8. BZOJ 1734: [Usaco2005 feb]Aggressive cows 愤怒的牛( 二分答案 )

    最小最大...又是经典的二分答案做法.. -------------------------------------------------------------------------- #inc ...

  9. [BZOJ 2500]幸福的道路 树形dp+单调队列+二分答案

    考试的时候打了个树链剖分,而且还审错题了,以为是每天找所有点的最长路,原来是每天起点的树上最长路径再搞事情.. 先用dfs处理出来每个节点以他为根的子树的最长链和次长链.(后面会用到) 然后用类似dp ...

随机推荐

  1. 关于SubSonic3.0插件使用SqlQuery或Select查询时产生的System.NullReferenceException异常修复

    早上在编写执行用例时,突然爆异常System.NullReferenceException: 未将对象引用设置到对象的实例 执行代码:

  2. Java 批量插入数据(Oracle)

    //批量添加20000条数据用时8秒. try {    String url = "jdbc:oracle:thin:@IP:1521:orcl"; // orcl为数据库的SI ...

  3. 浅谈sql的字符分割

    对于oracle:在字符串处理时:经常会遇到字符串分割的问题:可惜SQL中没有split函数:这个倒是挺困扰我们写sql的.对此:我来说说这字符串分割. 例如对字段str中一条数据是'120-mm-2 ...

  4. 还是this的问题

    var name = "The Window";    var object = {    name : "My Object",    getNameFunc ...

  5. 用Pyinstaller打包发布exe应用 (转)经测可用

    安装Pyinstaller   1 按照习惯,我们使用pip来安装模块.我们一直以来强调,要用最偷懒的方法.写代码的人尤其如此.人生苦短,你要偷懒~   0Python | 如何用pip安装模块和包 ...

  6. discuz MVC结构分析

    Discuz软件经解压后产生的三个文件夹中的一个叫upload的成为网站的根目录.里面的内容可以在某些网站上在线阅读,如用好库编程网.也可以离线在本地阅读,如用VS.Php for Visual St ...

  7. 解决Django发送中文邮件时的编码及乱码问题

    参考自---http://blog.csdn.net/clh604/article/details/9274793 #-*- coding=utf8 -*- from email.message im ...

  8. 完数[HDU1406]

    完数 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...

  9. 虚拟机centos6.5 --VirtualBox设置全屏

    一.安装以下模块 yum install kernel-devel kernel-headers gcc,然后重启. 二.安装增强功能 安装失败,查看日志文件,cat /var/log/vboxadd ...

  10. (转)女生应该找一个玩ACM的男生

    1.强烈的事业心 将来,他也一定会有自己热爱的事业.而且,男人最性感的时刻之一,就是他专心致志做事的时候.所以,找一个机会在他全神贯注玩ACM的时候,从侧面好好观察他,你就会发现我说的话没错. 2.永 ...