P2468 [SDOI2010]粟粟的书架 动态规划,主席树,二分答案

题目

洛谷题目链接

题解

题目的数据范围非常奇怪,一半是 200 200 的矩阵,另一半是 1 50000 的矩阵,显然是一道二合一的题目,但是不一样的地方也就在与数据结构的选择不一样,而我们解这道题使用的算法是一样的,即都是二分法。

这道题所需要的数据结构应该能提供如下的功能:在 O ( 1 ) O ( l o g n ) 的时间复杂度内查询矩阵某区间内厚度大于 x 的书本数,以及厚度大于 x 的书的页数总和。

根据上述数据结构的功能,我们可以设计一个二分算法。

即我们二分我们选取的书的最小厚度 m i d c h e c k 时候就 c h e c k 一下矩阵内厚度 m i d 的所有书的厚度之和是否能达到要求。
这样的话,二分完之后,所有厚度大于 l 的书的个数就是我们的答案!
错!因为有可能厚度等于 l 的书被多余使用了,因此,我们还要去掉一部分厚度为 l 的书,使得总厚度仍然满足要求,但是答案变小。

算法我们已经涉及完了。
现在我们想一下怎么样设计数据结构:
当矩阵大小为 200 200 的时候,我们定义 v a l [ i ] [ j ] [ k ] 表示矩阵区间 [ 1 , i ] [ 1 , j ] 部分,厚度 k 的书的总厚度,类似的定意 n u m [ i ] [ j ] [ k ] 表示个数。
这样只需要 d p 一下就ok了。

当矩阵大小为 1 500000 的时候,涉及到区间操作,我们可以使用两棵主席树,一颗维护和,另一颗维护个数。

代码

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define pr(x) cout<<#x<<":"<<x<<endl
using namespace std;
const int maxn = 500007;
struct segtree{
    int root[maxn*20];
    int val[maxn*20];
    int lson[maxn*20];
    int rson[maxn*20];
    int id = 0;
    void init(){
        memset(root,0,sizeof(root));
        memset(val,0,sizeof(val));
        memset(lson,0,sizeof(lson));
        memset(rson,0,sizeof(rson));
        id = 0;
    }
    int ins(int &rt,int l,int r,int pos,int v){
        int nrt = ++id;
        lson[nrt] = lson[rt];
        rson[nrt] = rson[rt];
        val[nrt] = val[rt] + v;
        rt = nrt;
        if(l == r) return 0;
        int mid = (l+r)/2;
        if(pos <= mid) ins(lson[rt],l,mid,pos,v);
        else ins(rson[rt],mid+1,r,pos,v);
    }
    int query(int rt,int l,int r,int ul,int ur){
        if(!rt || ul > r || ur < l) return 0;
        if(ul <= l && r <= ur) return val[rt];
        int mid = (l+r)/2;
        int r1 = query(lson[rt],l,mid,ul,ur);
        int r2 = query(rson[rt],mid+1,r,ul,ur);
        return r1+r2;
    }
}*seg,*segcnt;
int R,C,M;
int ck1(int mid,int y1,int y2){
    int sm = seg->query(seg->root[y2],1,1000,mid,1000);
        sm -= seg->query(seg->root[y1-1],1,1000,mid,1000);
    return sm;
}
void solve1(){
    seg = new segtree;
    segcnt = new segtree;
    seg->init();
    segcnt->init();
    for(int i = 1;i <= C;++i){
        int v;scanf("%d",&v);
        seg->root[i] = seg->root[i-1];
        segcnt->root[i] = segcnt->root[i-1];
        seg->ins(seg->root[i],1,1000,v,v);
        segcnt->ins(segcnt->root[i],1,1000,v,1);
    }

    for(int i = 1;i <= M;++i){
        int x1,y1,x2,y2,h;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
        int l = 1,r = 1000;
        while(l < r){
            int mid = (l+r+1) / 2;
            if(ck1(mid,y1,y2) >= h) l = mid;
            else r = mid-1;
        }
        if(ck1(l,y1,y2) < h) puts("Poor QLW");
        else {
            int ans = segcnt->query(segcnt->root[y2],1,1000,l,1000);
                ans -= segcnt->query(segcnt->root[y1-1],1,1000,l,1000);
            int delta = ck1(l,y1,y2) - h;
            ans -= delta / l;
            printf("%d\n",ans);
        }
    }
}
int (*val)[201][1001],(*vc)[201][1001];
int ck2(int va[201][201][1001],int mid,int x1,int y1,int x2,int y2){
    int ans = va[x2][y2][mid] + va[x1-1][y1-1][mid]
        - va[x2][y1-1][mid] - va[x1-1][y2][mid];
    return ans;
}
void solve2(){
    val = new int[201][201][1001];
    vc = new int[201][201][1001];
    memset(val,0,sizeof(val));
    memset(vc,0,sizeof(vc));

    for(int i = 1;i <= R;++i) for(int j = 1;j <= C;++j){
        int tmp;scanf("%d",&tmp);
        for(int k = 1;k <= 1000;++k){
            val[i][j][k] = val[i][j-1][k] + val[i-1][j][k]
                            - val[i-1][j-1][k];
            vc[i][j][k] = vc[i][j-1][k] + vc[i-1][j][k] - vc[i-1][j-1][k];
            if(tmp >= k) val[i][j][k] += tmp,vc[i][j][k] ++;   
        }
    }
    for(int i = 1;i <= M;++i){
        int x1,y1,x2,y2,h;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
        int l = 1,r = 1000;
        while(l < r){
            int mid = (l+r+1) / 2;
            if(ck2(val,mid,x1,y1,x2,y2) >= h) l = mid;
            else r = mid-1;
        }
        if(ck2(val,l,x1,y1,x2,y2) < h) puts("Poor QLW");
        else {
            int ans = ck2(vc,l,x1,y1,x2,y2);
            int delta = (ck2(val,l,x1,y1,x2,y2)-h)/l;
            ans -= delta;
            printf("%d\n",ans);
        }
    }
}

int main()
{
    cin>>R>>C>>M;
    if(R == 1) solve1();
    else solve2();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_37517391/article/details/80228129