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

    [Code+#3]寻找车位 

    挺厉害的线段树题


    m<=n,所以n<=2000,并且只有1000次修改询问,mqlogn的复杂度可以接受!

     求全局?

    对行(n)建一个线段树。

    线段树中维护的东西,一定可以包含所有“完全包含在”这个横条中的最大正方形。

    只在mid左、右的可以递归下去再取max,跨越中间的?

    大小为1000的两个数组,维护区间两端的1000个位置的从左从右开始最长1的个数

    线段树pushup的时候 考虑跨过mid的正方形

    不妨考虑长方形 宽<=长 纵向下来的是宽,双指针l,r 横向的是长 不断往后走r 如果r-l+1>lsmin(l,r)+rsmin(l,r) 那么++l 保证宽小于等于长

    (宽大于长的会在宽小的时候统计到) 相当于枚举对于r的时候,最靠上的宽小于等于长的位置l (因为ans取决于短边,也就是宽) 一定有单调性,所以l直接++即可 lsmin(l,r)+rsmin(l,r)额外用单调队列维护

    子矩形?

    先通过线段树把子矩形劈成logn段,就暂时消除了行宽的限制

    直接做的话,对于完整的一个线段树区域,还要暴力枚举每个行中线的,就O(q*n^2logn)了

    然鹅

    对于每一个中线mid,设之前单调队列找到的边长是r[x][i],那么就是min(r[x][i],i-U+1)来贡献答案。(U,D是列的限制)

    都是对i-U+1取min,那么r[x][i]一定就是选择最大的那一个。

    所以,

    每个区间再维护一个R[i],所有r[i]的max

    这样保证了子矩形分到完整的区间的时候,可以直接做完return了。复杂度就能正确

    对于query时往两侧都分治的区间,要再统计跨区间的最大正方形

    这时最大1的长度就要和行的限制取min了。

    queue用个pair存

    代码:

    (动态分内存)

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    #define mid ((l+r)>>1)
    #define ls (x<<1)
    #define rs (x<<1|1)
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=4e6+10;
    int n,m,q;
    int *lmx[4*N],*rmx[4*N],*ans[4*N];
    int Pool[14*N],*cur=Pool,*mp[N];
    struct que{
        int l,r;
        pair<int,int>q[N];
    //    void p_f(int L){
    //        while(l<=r&&q[l]<L) ++l;
    //    }
        void push(int id,int v){
            while(l<=r&&q[r].se>v) --r;
            q[++r]=make_pair(id,v);
        }
        int get(int L){
            while(l<=r&&q[l].fi<L) ++l;
            if(l<=r) return q[l].se;
            else return -1;
        }
        void clear(){
            l=1,r=0;
        }
    }Q1,Q2;
    void pushup(int x,int l,int r){
        Q1.clear();Q2.clear();
        int j=1;
        for(reg i=1;i<=m;++i){
            Q1.push(i,rmx[ls][i]);
            Q2.push(i,lmx[rs][i]);
            while(i>=j&&Q1.get(j)+Q2.get(j)<i-j+1){
                ++j;
            }
            ans[x][i]=i-j+1;
        }
        for(reg i=1;i<=m;++i){
            ans[x][i]=max(ans[x][i],max(ans[ls][i],ans[rs][i]));
            lmx[x][i]=(lmx[ls][i]==mid-l+1)?lmx[ls][i]+lmx[rs][i]:lmx[ls][i];
            rmx[x][i]=(rmx[rs][i]==r-mid)?rmx[rs][i]+rmx[ls][i]:rmx[rs][i];
        }
    }
    void build(int x,int l,int r){
        lmx[x]=cur;cur+=m+1;
        rmx[x]=cur;cur+=m+1;
        ans[x]=cur;cur+=m+1;
        
        if(l==r){
            for(reg i=1;i<=m;++i){
                ans[x][i]=rmx[x][i]=lmx[x][i]=mp[l][i];
            }
            return;
        }
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
        pushup(x,l,r);
    }
    void upda(int x,int l,int r,int p,int t,int c){
        if(l==r){
            ans[x][t]=rmx[x][t]=lmx[x][t]=c;
            return;
        }
        if(p<=mid) upda(x<<1,l,mid,p,t,c);
        else upda(x<<1|1,mid+1,r,p,t,c);
        pushup(x,l,r);
    }
    int mx;
    void merge(int x,int l,int r,int L,int R,int U,int D){
        Q1.clear();Q2.clear();
        int j=U;
        for(reg i=U;i<=D;++i){
            Q1.push(i,min(mid-L+1,rmx[ls][i]));
            Q2.push(i,min(R-mid,lmx[rs][i]));
            while(i>=j&&Q1.get(j)+Q2.get(j)<i-j+1){
                ++j;
            }
            mx=max(mx,i-j+1);
        }
    }
    void query(int x,int l,int r,int L,int R,int U,int D){
        if(L<=l&&r<=R){
            for(reg i=U;i<=D;++i){
                mx=max(mx,min(ans[x][i],i-U+1));
            }
            return;
        }
        if(L<=mid&&mid<R) {
            query(x<<1,l,mid,L,R,U,D);
            query(x<<1|1,mid+1,r,L,R,U,D);
            merge(x,l,r,L,R,U,D);
        }else if(L<=mid){
            query(x<<1,l,mid,L,R,U,D);
        }else{
            query(x<<1|1,mid+1,r,L,R,U,D);
        }
    }
    
    int main(){
        rd(n);rd(m);rd(q);
        Q1.clear();Q2.clear();
        for(reg i=1;i<=n;++i){
            mp[i]=cur;cur+=m+1;
            for(reg j=1;j<=m;++j){
                rd(mp[i][j]);
            }
        }
        build(1,1,n);
        int x,y,l,s,r,t;
        int op;
        while(q--){
            rd(op);
            if(op==0){
                rd(x);rd(y);
                mp[x][y]^=1;
                upda(1,1,n,x,y,mp[x][y]);
            }else{
                mx=0;
                rd(l);rd(s);rd(r);rd(t);
                query(1,1,n,l,r,s,t);
                printf("%d
    ",mx);
            }
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/2/13 21:22:39
    */

    总结:
    抓住m<=n性质,对长的n建线段树,区间维护信息时候,处理跨域mid的最大正方形。

    灵活运用单调队列。

  • 相关阅读:
    几种排序算法比较
    VB 增强的部件与引用
    EXCEL表格常用函数使用的难点
    VBA取得EXCEL表格中的行数和列数
    VB指针 与CopyMemory
    【VB】StrConv函数 vbUnicode用法
    TCP/IP笔记(七)TCP详解
    TCP/IP笔记(六)TCP与UDP
    TCP/IP笔记(四)IP协议
    TCP/IP笔记(三)数据链路层
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10372696.html
Copyright © 2011-2022 走看看