zoukankan      html  css  js  c++  java
  • 寻找车位[Code+#3]

    题目描述

    access_globe 有一个巨大的停车场,这个停车场有 (N) 行,每行有 (M) 个车位。为了美观,access_globe 在建立这个停车场时,规定这个停车场必须是长条形的,即 (Nge M) 。每个车位都是一个正方形的区域。

    最近,access_globe 正在为抽不到 Missing Poster 而苦恼,因此他请你帮他维护这个停车场。你需要支持两个事件:

    • 一辆车停到某一个车位中,或一辆车从某个车位开走
    • 查询一个矩形区域内最大的只包含空车位的正方形区域

    如果你能帮 access_globe 高效地解决这个问题,access_globe 一定会好好奖励你的。

    (N imes M le 4*10^6,Qle 2000)

    题解

    线段树神题

    (N) 这一维开一棵线段树,然后线段树上的每个节点维护3个数组:lm[M], rm[M], val[M]

    假设线段树的某个节点表示的是 ([l,r]) 这段区间 ((1le lle rle N)) ,那么这个节点的 lm[i] 表示第 (i) 列中,从第 (l) 个车位开始往后数有多少个连续的 (1)rm[i] 表示从第 (r) 个车位往前数有多少个连续的 (1)

    这个节点的 val[i] 表示如果以第 (i) 列作为正方形的右边界,且正方形的上下边界在 ([l,r]) 内 (这里的 (l,r) 实际上是竖着的一段区间) ,全1的正方形的边长最大是多少

    lm[M]rm[M] 都很容易通过左右儿子的值求出来,如何求出 val[M] ?

    假设 (mid=dfrac{l+r}{2}),先考虑这个正方形的上下边界都在 ([l,mid]) 或者都在 ([mid+1,r])

    显然此时 val[i] 等于左右儿子的 val[i] 的较大值

    如果正方形跨过了 (mid) 呢?

    假设我们现在已经知道了正方形的上下边界在 ([l,r]) 内,如果正方形的右边界在 (i),左边界在 (j) (即边长为 (i-j+1) ) ,如何判断是否存在合法的正方形?

    如图 我们对于 ([j,i]) 中的每一列,找到从第 (mid) 行开始有多少个向上延申的连续的 (1) (蓝线),同理找到向下延申的,然后分别找到向上和向下最少的 (绿线) , 记为 (a,b) ,如果 (i-j+1le a+b) ,就表示一定存在合法的正方形

    容易发现如果以 (i) 为右边界可以构成的最大全1正方形的左边界在 (j) ,那么以 (i+1) 为右边界可以构成的最大全1正方形的左边界不可能小于 (j)

    所以这个每次查询区间 ([j,i]) 的最小值可以使用单调队列来维护,枚举右端点 (i) 并且维护另外一个指针 (j) ,如果此时 (i-j+1) 大于上下两个最小值之和,那就说明不满足条件,(j) 继续右移,找到第一个满足条件的 (j) ,那么 ([l,r]) 这个线段树节点的 val[i] 就等于 (j)

    这样就通过合并左右儿子两个区间算出了当前节点的值了

    注意这样 pushup 一次的复杂度是 (O(m)) 的,所以进行一次修改或查询

    那么怎么查询答案呢?

    假设询问是 (x1,y1,x2,y2),将 ([x1,x2]) 在线段树上分为 (log N) 个区间,然后在线段树上合并这些区间即可

    一种比较聪明的做法是用一个没有用的节点暂时储存一下这 (log N) 个区间合并得到的答案,最后只需要找出这个临时节点 val[y1]val[y2] 之间的最大值即可

    代码

    #include <bits/stdc++.h>
    #define N 4000005
    using namespace std;
    
    template <typename T>
    inline void read(T &num) {
    	T x = 0, f = 1; char ch = getchar();
    	for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
    	for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ '0');
    	num = x * f;
    }
    
    int n, m, Q;
    int len[N<<2], q1[N], q2[N];
    vector<int> A[N];
    int lm[N<<2], rm[N<<2], val[N<<2];
    
    inline int o(int x, int y) { return x*m+y; } 
    
    inline void merge(int ind, int ls, int rs) {
        int h1 = 1, h2 = 1, t1 = 0, t2 = 0;
        for (int r = 1, l = 1; r <= m; r++) {
            while (h1 <= t1 && rm[o(ls,q1[t1])] > rm[o(ls,r)]) t1--;
            q1[++t1] = r;
            while (h2 <= t2 && lm[o(rs,q2[t2])] > lm[o(rs,r)]) t2--;
            q2[++t2] = r;
            while (h1 <= t1 && h2 <= t2 && rm[o(ls,q1[h1])] + lm[o(rs,q2[h2])] < r-l+1) {
                if (q1[h1] == l) h1++;
                if (q2[h2] == l) h2++;
                l++;
            }
            val[o(ind,r)] = max(max(val[o(ls,r)], val[o(rs,r)]), r-l+1);
        }
        for (int i = 1; i <= m; i++) lm[o(ind,i)] = lm[o(ls,i)] + (lm[o(ls,i)]==len[ls]?lm[o(rs,i)]:0);
        for (int i = 1; i <= m; i++) rm[o(ind,i)] = rm[o(rs,i)] + (rm[o(rs,i)]==len[rs]?rm[o(ls,i)]:0);
    }
    void build(int ind, int l, int r) {
        len[ind] = r-l+1;
        if (l == r) {
            for (int i = 1; i <= m; i++) lm[o(ind,i)] = rm[o(ind,i)] = val[o(ind,i)] = A[l][i];
            return; 
        }
        int mid = (l + r) >> 1;
        build(ind<<1, l, mid); build(ind<<1|1, mid+1, r);
        merge(ind, ind<<1, ind<<1|1);
    }
    void update(int ind, int l, int r, int x, int y, int v) {
        if (l == r) {
            lm[o(ind,y)] = rm[o(ind,y)] = val[o(ind,y)] = v;
            return; 
        }
        int mid = (l + r) >> 1;
        if (x <= mid) update(ind<<1, l, mid, x, y, v);
        else update(ind<<1|1, mid+1, r, x, y, v);
        merge(ind, ind<<1, ind<<1|1);
    }
    
    void query(int ind, int l, int r, int x, int y) {
        int ret = 0;
        if (x <= l && r <= y) {
            merge(0, 0, ind); //用0号点暂时储存答案
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid) query(ind<<1, l, mid, x, y);
        if (mid < y) query(ind<<1|1, mid+1, r, x, y);
    }
    
    int main() {
        read(n); read(m); read(Q);
        for (int i = 1; i <= n; i++) A[i].resize(m+1);
        for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) {
            read(A[i][j]);
        }
        build(1, 1, n);
        for (int i = 1, tp, x, y, a, b; i <= Q; i++) {
            read(tp);
            if (tp == 0) {
                read(x); read(y);
                if (A[x][y]) update(1, 1, n, x, y, 0);
                else update(1, 1, n, x, y, 1);
                A[x][y] = 1 - A[x][y];
            } else {
                read(x); read(y); read(a); read(b);
                for (int j = 1; j <= m; j++) {
                    lm[o(0,j)] = rm[o(0,j)] = val[o(0,j)] = 0; //用0号点暂时储存答案
                }
                query(1, 1, n, x, a);
                int ans = 0;
                for (int j = y; j <= b; j++) {
                    ans = max(ans, min(j-y+1, val[o(0, j)]));
                }
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    iis6|iis7|配置URLRewriter|64位操作系统下|.net2.0|.net4.0|配置URLRewriter|Web.config配置详情
    如何用Fiddler对Android应用进行抓包
    asp.net写验证码
    Linq to sql 语法方法示例
    asp.net正则表达式学习例子
    js 根据年月获取当月有多少天_js获取农历日期_及Js其它常用有用函数
    Sql Server 相关错误问题及解决方法
    Javascript 添加自定义静态方法属性JS清除左右空格
    asp.net 文件批量移动重命名
    Memcached监听多个端口_同一台Windows机器中启动多个Memcached服务
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM111.html
Copyright © 2011-2022 走看看