zoukankan      html  css  js  c++  java
  • 【洛谷4259】[Code+#3] 寻找车位(奇怪的线段树题)

    点此看题面

    • 给定一张(n imes m)(01)矩形,支持两种操作:
      • 单点修改。
      • 询问一个子矩形内最大全(1)正方形边长。
    • 操作次数(le2 imes10^3,n imes mle4 imes10^6(nge m))

    奇怪的线段树

    考虑(n imes mle4 imes10^6)说明(mle2 imes10^3)

    既然(m,q)都很小,那么我们完全可以去构思一个(O(mqlogn))的做法。

    (1)正方形的常规解法是单调队列,现在询问子矩形,我们可以使用一个以为下标的线段树维护。

    具体地,线段树上每个节点开三个长度为(m)的数组(Up,Dn,V),分别表示这些行内每一列上方连续(1)的个数、下方连续(1)的个数、以这列为右端点的答案

    (PushUp)的时候(Up)(Dn)的信息是非常容易上传的,(V)的上传就像先前提到的那样,采用单调队列即可,相信大家都会(实在不会可以看代码,有注释)。

    奇怪的数组

    注意这题只给出了(n imes m)的大小,数组不能乱开。

    考虑开(vector),不知道是不是我实现不够优秀,尽管是(1000MB)的内存,依旧华丽地(MLE)了。。。

    所以我们采用指针,把数组开成这个样子:

    struct Array {int v[NM<<2];I int* operator [] (CI x) {return v+x*m;}};
    

    然后就能和平时一样地使用数组了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define NM 4000000
    #define M 2000
    using namespace std;
    int n,m;struct Array {int v[NM<<2];I int* operator [] (CI x) {return v+x*m;}}a;//奇怪的数组
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    class SegmentTree
    {
    	private:
    		#define PT CI l=1,CI r=n,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		#define PU(x) Merge(rt,rt<<1,rt<<1|1,mid-l+1,r-mid)//上传信息就是合并两个子区间
    		int q1[M+5],q2[M+5];Array Up,Dn,V;
    		I void Merge(CI rt,CI A,CI B,CI L,CI R)//合并A,B信息到rt
    		{
    			RI i,j,H1=1,T1=0,H2=1,T2=0;for(i=j=1;i<=m;++i)//枚举右端点
    			{
    				W(H1<=T1&&Dn[A][q1[T1]]>Dn[A][i]) --T1;q1[++T1]=i;//第一个单调队列,维护上面的行下方连续的1的个数递减
    				W(H2<=T2&&Up[B][q2[T2]]>Up[B][i]) --T2;q2[++T2]=i;//第二个单调队列,维护下面的行上方连续的1的个数递减
    				W(i-j+1>Up[B][q2[H2]]+Dn[A][q1[H1]]) q1[H1]^j||++H1,q2[H2]^j||++H2,++j;//如果列数大于行的答案,移动左端点
    				V[rt][i]=max(max(V[A][i],V[B][i]),i-j+1);//答案是两个子矩阵答案与当前求出答案的较大值
    			}
    			for(i=1;i<=m;++i)//上传Up和Dn信息,注意不能并到上面(求解询问时rt=A,会影响到A的值)
    				Up[rt][i]=Up[A][i]+(Up[A][i]^L?0:Up[B][i]),//如果是满的,下面的行可以接上
    				Dn[rt][i]=Dn[B][i]+(Dn[B][i]^R?0:Dn[A][i]);//如果是满的,上面的行可以接上
    		}
    	public:
    		I void Build(PT)//建树
    		{
    			if(l==r) {for(RI i=1;i<=m;++i) Up[rt][i]=Dn[rt][i]=V[rt][i]=a[l][i];return;}
    			RI mid=l+r>>1;Build(LT),Build(RT),PU(rt);
    		}
    		I void U(CI x,CI y,CI v,PT)//单点修改
    		{
    			if(l==r) return (void)(Up[rt][y]=Dn[rt][y]=V[rt][y]=v);
    			RI mid=l+r>>1;x<=mid?U(x,y,v,LT):U(x,y,v,RT),PU(rt);
    		}
    		I void G(CI L,CI R,PT)//找到询问的那些行
    		{
    			if(L<=l&&r<=R) return Merge(0,0,rt,l-L,r-l+1);//总是先访问左边,依次得到已询问行的总大小
    			RI mid=l+r>>1;L<=mid&&(G(L,R,LT),0),R>mid&&(G(L,R,RT),0);
    		}
    		I int Q(CI x,CI y,CI u,CI v)//询问
    		{
    			RI i,t=0;for(i=1;i<=m;++i) Up[0][i]=Dn[0][i]=V[0][i]=0;
    			for(G(x,u),i=y;i<=v;++i) t=max(t,min(V[0][i],i-y+1));return t;//枚举询问每一列,注意不能超出询问左端点
    		}
    }S;
    int main()
    {
    	RI Qt,i,j,x,y;for(F.read(n,m,Qt),i=1;i<=n;++i) for(j=1;j<=m;++j) F.read(a[i][j]);
    	RI op,u,v;S.Build();W(Qt--) F.read(op,x,y),
    		op?(F.read(u,v),printf("%d
    ",S.Q(x,y,u,v))):(S.U(x,y,a[x][y]^=1),0);return 0;
    }
    
  • 相关阅读:
    160309_Qt Essentials
    160309_Qt Reference Documentation
    160308_Signals & Slots
    160308_Helloworld_Gui Application
    网络爬虫(14)-动态页面爬取
    数据分析(6)-Pandas日期数据处理
    mysql基础(2)-excel功能在excel中如何实现?
    数据分析(5)-数据可视化常用图表类型和使用场景
    财经数据(6)-Python多进程爬虫东方财富个股盘口异动数据
    财经数据(5)-开盘啦股票标签数据爬虫
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4259.html
Copyright © 2011-2022 走看看