安徽师大附中%你赛day4T2 演讲解题报告
演讲
题目背景:
众所周知,\(\mathrm{Zdrcl}\)是一名天天\(\mathrm{AK}\)的高水平选手。
作为一民长者,为了向大家讲述自己\(\mathrm{AK}\)的经验,他决定在一个礼堂里为大家举办一场演讲。
这个礼堂拥有\(N\times M\)个位子,排成\(N\)行\(M\)列。每个位子都有一盏灯,一开始有的灯是亮的,有的灯是灭的。这个礼堂十分诡异,人们操作一次只能使某一行或某一列(某一行或某一列由操作者你自己来决定)的灯的明暗状态全都发生转变(显然,我们不一定可以把所有的灯都点亮)。
来听演讲的人只会坐在灯已经被点亮的位置, 所以可以听演讲的位置只会是一个只由灯是亮的的位子所组成的矩形(不然坐太乱,\(\mathrm{Zdrcl}\)会不高兴的) 。
\(\mathrm{Zdrcl}\)知道会有很多人来听演讲, 所以他希望找到一个经过若干操
作后的面积最大的只由灯是亮的的位子所组成的矩形。 这个任务当然由想\(\mathrm{AK}\)的你来完成啦!
输入输出格式
输入格式:
第一行两个正整数表示\(N,M\)。
接下来有\(N\)行,每行有\(M\)个字符(‘#’表示这个灯初始状态是亮的,‘.’表示这个灯初始状态是暗的)。
输出格式
一行一个整数表示你找到的矩形的面积。
数据范围
对于\(5\%\)的数据:\(N=2,M=2\)
对于\(15\%\)的数据:\(N\times M\le 8\)
对于\(30\%\)的数据:\(N,M\le 10\)
对于\(60\%\)的数据:\(N\le 1\le 10^2\)
对于\(80\%\)的数据:\(N\le 4\times 10^2\)
对于\(100\%\)的数据:\(N\le 2\times 10^3\)
输入文件比较大, 请使用比较快速的读入方法。
提示
这一题写起来不是很困难。
这一题确实不算难
主要就是“手玩手玩,再手玩”
如果我们的某一个矩形是答案矩形,那么它一定会满足什么呢?
假设现在我们已经做完了行操作得到了中间状态,那么中间状态如何指经过列操作得到答案矩形呢?
我们再探究探究如何只通过列操作得到中间状态呢?
手玩以后,我们发现,如果一个原始的01矩阵相邻行异或之后,每一行连续的0,1即是合法的选取
这里就转换成了一个取矩形的问题,可以使用单调栈进行优化
Code:
#include <cstdio>
#include <bitset>
using namespace std;
const int N=2010;
bitset <N> a[N],d[N];
int max(int x,int y){return x>y?x:y;}
int n,m,ans;char c;
int f[N][N],L[N],R[N],s[N],tot;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("\n");
for(int j=1;j<=m;j++)
{
scanf("%c",&c);
if(c=='#') d[i][j-1]=1;
}
}
for(int i=2;i<=n;i++)
a[i]=d[i]^d[i-1];
for(int i=1;i<=n;i++)
{
f[i][m]=1;
for(int j=m-1;j;j--)
{
if(a[i][j-1]==a[i][j])
f[i][j]=f[i][j+1]+1;
else
f[i][j]=1;
}
}
for(int i=1;i<=m;i++)
{
tot=0;
for(int j=2;j<=n;j++)
{
L[j]=1;
while(tot&&f[s[tot]][i]>=f[j][i]) L[j]+=L[s[tot--]];
s[++tot]=j;
}
tot=0;
for(int j=n;j>1;j--)
{
R[j]=1;
while(tot&&f[s[tot]][i]>=f[j][i]) R[j]+=R[s[tot--]];
s[++tot]=j;
}
for(int j=2;j<=n;j++)
ans=max(ans,f[j][i]*(R[j]+L[j]));
}
printf("%d\n",ans);
return 0;
}
2018.8.16