题解 P6600 【「EZEC-2」字母T】
蒟蒻作为出题人
据比赛官方透露,这个题是经过加强的
在加强之前,字母T是没有限制的(即
所以咱们就从这种情况入手,这样有利于向正解进发!
分析一下字母T的组成吧……
可以发现:对于图中的每一个含
设这个点左,右,下三个方向连续
所以我们可以先把每个点的
但是怎么处理呢,一个个找?
不,我们用前缀和。
直接上代码吧!(话说
bool wyx[3005][3005];//题目给的01矩阵(wyx是我们的红太阳!!!)
long long sl[3005][3005],sr[3005][3005],sd[3005][3005];//l,r,d(开 long long 啊awa)
//如果这里是1,那就加上前面的
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
if(wyx[i][j])sl[i][j]=sl[i][j-1]+1;
}
//后面的亦如此,但是要注意一下循环顺序
for(int i=1;i<=n;i++){
for(int j=m;j>0;j--)
if(wyx[i][j])sr[i][j]=sr[i][j+1]+1;
}
for(int i=n;i>0;i--){
for(int j=1;j<=m;j++)
if(wyx[i][j])sd[i][j]=sd[i+1][j]+1;
}
这样的话,我们就完美迈出了AC的第一步!
接下来,咱们来考虑
对于每个
w ,我们可以算出最小的合法h 。 ——君のNOIP。
那怎么算呢?我们来看看这四个条件:
因为都要满足,所以我们从三个值里面要取个最大的,即:
long long minh[3005];//minh:对于每个w,它的最大合法h
for(int w=a+!(a&1);w<=m;w+=2)//因为w>=a,所以只需要从a开始,加!(a&1)是为了保证w为奇数
这样的话,对于任意情况我们都可以通过枚举中心点和
但是……这貌似还不太够……
接下来就是最后一战了:我们要把
往深入里思考一下,我们需要把
不难看出:
然后再来考虑
但是
别急嘛,既然前面我们已经推出了对于每个
既然我们已经推出了
int w=a+!(a&1);//w还是从a开始
for(int h=n;h>=b;h--){//由于w越大,h越小,所以我们让h从大到小推
while(w<=m&&minh[w]>h)w+=2;//增加w到minh[w]<=h为止
minw[h]=w;//这就是minw的值啦
}
这样的话,我们就把
不过,这里面还有很多不符合要求的情况,怎么办呢?
看图~
蓝色阴影的
绿色的
红色圈圈的
也就是说:
也就是说,我们只需要求出
那怎么求呢?我们观察一下
这不就是
所以:
貌似是一段……区间和?
那就简单了:还是用前缀和来解决问题!
先处理出
sminh[w]=sminh[w-2]+minh[w];//因为w只能是奇数所以是-2
然后再用刚才的结论解决问题:
long long ans=0;//注意long long
for(int i=1;i<=n;i++){//枚举中心点
for(int j=1;j<=m;j++){
if(!wyx[i][j])continue;//只有这里是1的时候才参加统计
long long h=sd[i][j];//h的最大范围(为了后面的invalid区域好算,h的下界直接变成0了,反正到头来还要被减掉)
long long lw=minw[h],rw=1ll*2*min(sl[i][j],sr[i][j])-1;//w的最小范围和最大范围
if(rw>=lw&&h>=b)//要保证有答案才能算(否则你整出来个负数是啥情况)
ans+=1ll*(rw-lw+2)/2*(h+1)-(sminh[rw]-sminh[lw-2]);//answer=all-invalid,一些+1,-2这样的细节需要注意下
}
}
完整代码:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
bool wyx[3005][3005];
long long sl[3005][3005],sr[3005][3005],sd[3005][3005];
long long minh[3005],sminh[3005],minw[3005];
int n,m,a,b,s,x;
long long ans=0;
int main(){
ios::sync_with_stdio(false);//关闭同步流
cin.tie(0);cout.tie(0);
cin>>n>>m>>a>>b>>s>>x;
a=max(a,3),b=max(b,2);//说好的w>=3,h>=2
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
char c;
cin>>c;
wyx[i][j]=c-'0';//输入
}
}
//算出三个方向的前(后)缀和
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
if(wyx[i][j])sl[i][j]=sl[i][j-1]+1;
}
for(int i=1;i<=n;i++){
for(int j=m;j>0;j--)
if(wyx[i][j])sr[i][j]=sr[i][j+1]+1;
}
for(int i=n;i>0;i--){
for(int j=1;j<=m;j++)
if(wyx[i][j])sd[i][j]=sd[i+1][j]+1;
}
int w;
for(w=a+!(a&1);w<=m;w+=2){
minh[w]=max(b,max(x-w,(int)(ceil(s*1.0/w))));//运用限制条件算出minh
sminh[w]=sminh[w-2]+minh[w];
}
w=a+!(a&1);
for(int h=n;h>=b;h--){
while(w<=m&&minh[w]>h)w+=2;//运用minh算出minw
minw[h]=w;
}
long long ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(!wyx[i][j])continue;
long long h=sd[i][j];
long long lw=minw[h],rw=1ll*2*min(sl[i][j],sr[i][j])-1;
if(rw>=lw&&h>=b)
ans+=1ll*(rw-lw+2)/2*(h+1)-(sminh[rw]-sminh[lw-2]);//分析算出ans(实际上可以说是容斥原理)
}
}
cout<<ans;
}
最后的最后:
-
这个题的前身:U95387 TLE
-
yrz最近的心里话,真的……