• [IOI2019]矩形区域


    题意:
    给一个(n×m)矩阵,问有多少子矩阵,对于其中任意一个数,都满足它小于它的上下左右四个方向中第一个在矩阵外面的数。
    (1leq n,mleq 2500)

    写一个(O(nmlognm))的做法。

    首先,对于每一行,每一列,分别求出其中的合法区间。
    显然,只要区间中的最大值满足条件(即它小于区间外侧的两个数)即可。
    因此,我们可以枚举最大值,用单调栈求出两侧不大于它的区间((l,r)),那么,只有((l-1,r+1))可能满足条件。
    这样,每个最大值只会贡献一个区间,因此总的区间数是(O(nm))的。

    求出这部分后,只要子矩阵的每行每列都能对应一个合法区间,它就是合法的了。
    再求出每个子区间向下(向右)能延伸的长度,这个可以边扫描,边更新。

    求答案时,我们可以枚举这个子矩阵最上边的那行,并枚举那行中的合法区间,设它的向下延伸长度为(x),区间长度为(y)
    之后,再枚举它的左端点的列中,上端点位于这行的一个区间。
    那么,只要这个区间长度不大于(x),向右延伸长度不小于(y),就会产生一个答案。
    可以发现,这是一个类似二维数点的问题,直接扫描线+树状数组维护即可。

    注意细节
    代码:

    #include <stdio.h>
    #include <vector>
    using namespace std;
    struct SJd
    {
    	int l,r;
    	SJd(){}
    	SJd(int L,int R)
    	{
    		l=L;r=R;
    	}
    };
    int getlr(int sz[2502],int n,SJd jg[2502])
    {
    	int l[2502],r[2502],m=0;
    	for(int i=0;i<n;i++)
    		l[i]=r[i]=i;
    	for(int i=0;i<n;i++)
    	{
    		while(l[i]>0&&sz[l[i]-1]<=sz[i])
    			l[i]=l[l[i]-1];
    	}
    	for(int i=n-1;i>=0;i--)
    	{
    		while(r[i]+1<n&&sz[r[i]+1]<sz[i])
    			r[i]=r[r[i]+1];
    	}
    	for(int i=0;i<n;i++)
    	{
    		if(l[i]>0&&r[i]+1<n&&sz[r[i]+1]>sz[i])
    			jg[m++]=SJd(l[i],r[i]);
    	}
    	return m;
    }
    SJd ha[2502][2502],li[2502][2502];
    int sh[2502],sl[2502],sz[2502][2502];
    int dn[2502][2502],ri[2502][2502],dp[2502][2502];
    bool bk[2502][2502];
    struct SPx
    {
    	int j,ri;
    	SPx(){}
    	SPx(int J,int RI)
    	{
    		j=J;ri=RI;
    	}
    };
    vector<SPx> ve[2502][2502];
    int C[2502][2502],N;
    void add(int a,int i)
    {
    	i+=1;
    	for(int j=i;j>0;j-=j&(-j))
    		C[a][j]+=1;
    }
    void clean(int a,int i)
    {
    	i+=1;
    	for(int j=i;j>0;j-=j&(-j))
    		C[a][j]=0;
    }
    int sum(int a,int i)
    {
    	int jg=0;i+=1;
    	for(int j=i;j<=N;j+=j&(-j))
    		jg+=C[a][j];
    	return jg;
    }
    vector<int> vv[2510];
    int main()
    {
    	int n,m;
    	scanf("%d%d",&n,&m);N=m;
    	for(int i=0;i<n;i++)
    	{
    		for(int j=0;j<m;j++)
    			scanf("%d",&sz[i][j]);
    	}
    	for(int i=0;i<n;i++)
    		sh[i]=getlr(sz[i],m,ha[i]);
    	for(int i=0;i<m;i++)
    	{
    		int jh[2502];
    		for(int j=0;j<n;j++)
    			jh[j]=sz[j][i];
    		sl[i]=getlr(jh,n,li[i]);
    	}
    	for(int i=0;i<m;i++)
    	{
    		for(int j=0;j<m;j++)
    			dp[i][j]=-1;
    	}
    	for(int i=n-1;i>=0;i--)
    	{
    		for(int j=0;j<sh[i];j++)
    		{
    			int l=ha[i][j].l,r=ha[i][j].r;
    			dp[l][r]+=1;bk[l][r]=true;
    			dn[i][j]=dp[l][r];
    		}
    		if(i<n-1)
    		{
    			for(int j=0;j<sh[i+1];j++)
    			{
    				int l=ha[i+1][j].l,r=ha[i+1][j].r;
    				if(!bk[l][r])
    					dp[l][r]=-1;
    			}
    		}
    		for(int j=0;j<sh[i];j++)
    		{
    			int l=ha[i][j].l,r=ha[i][j].r;
    			bk[l][r]=false;
    		}
    	}
    	for(int i=0;i<n;i++)
    	{
    		for(int j=0;j<n;j++)
    			dp[i][j]=-1;
    	}
    	for(int i=m-1;i>=0;i--)
    	{
    		for(int j=0;j<sl[i];j++)
    		{
    			int l=li[i][j].l,r=li[i][j].r;
    			dp[l][r]+=1;bk[l][r]=true;
    			ri[i][j]=dp[l][r];
    		}
    		if(i<m-1)
    		{
    			for(int j=0;j<sl[i+1];j++)
    			{
    				int l=li[i+1][j].l,r=li[i+1][j].r;
    				if(!bk[l][r])
    					dp[l][r]=-1;
    			}
    		}
    		for(int j=0;j<sl[i];j++)
    		{
    			int l=li[i][j].l,r=li[i][j].r;
    			bk[l][r]=false;
    		}
    	}
    	for(int i=0;i<m-1;i++)
    	{
    		for(int j=0;j<sl[i];j++)
    			ve[li[i][j].l][li[i][j].r-li[i][j].l].push_back(SPx(i,ri[i][j]));
    	}
    	int ans=0;
    	for(int i=0;i<n;i++)
    	{
    		for(int j=0;j<n;j++)
    			vv[j].clear();
    		for(int j=0;j<sh[i];j++)
    			vv[dn[i][j]].push_back(j);
    		for(int k=0;k<n;k++)
    		{
    			for(int s=0;s<ve[i][k].size();s++)
    				add(ve[i][k][s].j,ve[i][k][s].ri);
    			for(int s=0;s<vv[k].size();s++)
    			{
    				int j=vv[k][s],l=ha[i][j].l,r=ha[i][j].r,z=r-l;
    				ans+=sum(l,z);
    			}
    		}
    		for(int k=0;k<n;k++)
    		{
    			for(int s=0;s<ve[i][k].size();s++)
    				clean(ve[i][k][s].j,ve[i][k][s].ri);
    		}
    	}
    	printf("%d",ans);
    	return 0;
    }
    
  • 相关阅读:
    行为科学统计第一章知识点总结
    JVM垃圾回收参数说明整理
    RestTemplate
    SparkContext源码阅读
    Spark RDD类源码阅读
    Scala学习笔记
    JAVA虚拟机类型转换学习
    工程开发实用类与方法总结(未完)
    JAVA 几种引用类型学习
    JAVA虚拟机垃圾回收算法原理
  • 原文地址:https://www.cnblogs.com/lnzwz/p/12327811.html
Copyright © 2020-2023  润新知