首页 > 其他分享 >CF364E Empty Rectangles

CF364E Empty Rectangles

时间:2022-12-14 22:13:11浏览次数:67  
标签:dque int Top long RG CF364E include Rectangles Empty

链接:https://www.luogu.com.cn/problem/CF364E

题目描述:你有一个\(n*m\)的\(01\)矩阵,现在询问在这其中有多少个子矩阵满足包含\(k\)个\(1\),即和为\(k\)。

题解:考虑枚举一个矩形在行坐标的左边界与右边界,那么原问题可以转化为一维问题,只需统计有多少个区间内部元素和恰好为\(k\),可以线性扫描解决,但这样的复杂度是\(O(m^2n)\)的。

考虑扫描线,枚举左边界扫右边界,每次将每一个右边界的\(1\)加入,每一次对一行\(+1\),我们发现一个\(1\)只有其到左边界的\(1\)小于等于\(<=k+1\)时才可能产生贡献。那么只要扫有贡献的\(1\),合法个数是\(O(nk)\)级别的。对于每一个\(1\),其增量是加入后跨过这一行的内部元素和恰好为\(k\)的区间个数\(-\)加入前的,那么可以用倍增树状数组求出合法区间个数,复杂度\(O(nmk^2logk)\),但常数太大无法通过。

由于进一步优化要\(O(1)\)查询前驱后继,考虑倒序扫描线,用链表维护,那么这样由于每一次加入一个点是复杂度是\(O(k)\)的,所以复杂度\(O(nmk^2)\)。

#include<iostream>
#include<cstdio>
#include<vector>
#include<cmath>
#include<ctime>
#define int long long
#define RG register 
#define N 3000
using namespace std;
int read()
{
	char c=0;
	int sum=0;
	while (c<'0'||c>'9') c=getchar();
	while ('0'<=c&&c<='9') sum=sum*10+c-'0',c=getchar();
	return sum;
}
vector<int>T[N+1];
long long n,m,k,leng,ans,delta,cnt[N+1],a[N+1][N+1],nxt[N+1][N+1],top[N+1][N+1],length[N+1],color[7*N+1];
int pv[7*N+1],nt[7*N+1],dque[N+1],f[N+1],l[N+1],r[N+1],Top;
long long A[N+1],B[N+1];
inline void solve(int x,int d,int op)
{
	int p1=x,p2=x,c;
	for (RG int q=0;q<=k;++q) A[q]=B[q]=0;
	while (pv[p1]!=0&&color[pv[p1]]==color[p1]) p1=pv[p1];
    if (op)
	{
		while (nt[p2]!=leng+1&&color[nt[p2]]==color[p2]) p2=nt[p2];
	}
	c=p2-p1+1;
	if (op==2) c=0;
	for (RG int q=0;q<=k-c&&p1!=0;++q) A[q]=color[p1]-color[pv[p1]],p1=pv[p1];
	for (RG int q=0;q<=k-c&&p2!=leng+1;++q) B[q]=color[nt[p2]]-color[p2],p2=nt[p2];
	if (op)
	{
		for (RG int q=0;q<=k-c;++q) delta+=d*A[q]*B[k-c-q];
	}
	else if (k>=c) delta+=d*A[k-c]*B[0];
	return;
}
signed main()
{
	char x;
	n=read(),m=read(),k=read();
	for (RG int i=1;i<=n;++i)
		for (RG int j=1;j<=m;++j)
			cin>>x,a[i][j]=(x=='1');
	for (RG int i=1;i<=n;++i)
		for (RG int j=m;j>=1;--j)
		{
			if (a[i][j]==1) top[i][++length[i]]=j;
			nxt[i][j]=length[i];
		}
	for (RG int i=1;i<=m;++i)
	{
		if (k==0)
		{
			Top=0;
			dque[Top=0]=0;
			for (RG int j=1;j<=n;++j)
			{
				if (nxt[j][i]==0) f[j]=m-i+1;
				else f[j]=top[j][nxt[j][i]]-i;
				while (Top>0&&f[dque[Top]]>=f[j]) Top--;
				l[j]=j-dque[Top],dque[++Top]=j;
			}
			dque[Top=0]=n+1;
			for (RG int j=n;j>=1;--j)
			{
				while (Top>0&&f[dque[Top]]>f[j]) Top--;
				r[j]=dque[Top]-j,dque[++Top]=j;
			}
			for (RG int j=1;j<=n;++j) ans+=l[j]*r[j]*f[j];
			continue;
		}
		delta=0,leng=0;
	    for (RG int j=0;j<=m;++j) T[j].clear();
		for (RG int j=1;j<=n;++j)
			for (RG int t=max(1ll,nxt[j][i]-k);t<=nxt[j][i];++t)
				T[top[j][t]].push_back(++leng),color[leng]=j;
		for (RG int j=1;j<=leng;++j) pv[j]=j-1,nt[j]=j+1;
		color[leng+1]=n+1;
		for (RG int j=1;j<=leng;++j) solve(j,1,0);
		ans+=delta;
		for (RG int j=m;j>=i+1;--j)
		{
			for (int t=0;t<T[j].size();++t)
			{
				solve(T[j][t],-1,1),nt[pv[T[j][t]]]=nt[T[j][t]],pv[nt[T[j][t]]]=pv[T[j][t]];
				if (color[pv[T[j][t]]]==color[T[j][t]]) solve(pv[T[j][t]],1,1);
				else if (color[nt[T[j][t]]]==color[T[j][t]]) solve(nt[T[j][t]],1,1);
				else solve(T[j][t],1,2);
			}
			ans+=delta;
		}
	}
	printf("%lld\n",ans);
	return 0;
}

标签:dque,int,Top,long,RG,CF364E,include,Rectangles,Empty
From: https://www.cnblogs.com/zhouhuanyi/p/16983775.html

相关文章