P13647 [NOISG 2016] Fabric 题解
Copyright_C · · 题解
下文中所说的 “高” 指所在行号更小,“低” 则相反。
首先考虑
很难推广到
注意到,每一列最高的洞的高度合起来组成了一个序列。若选定一个区间,提供限制的是这个区间中最高的洞。此时不难想到用单调栈维护出每个洞作为最高的洞的极长区间。把这个区间称为该点的管辖区间,那么对每个点只考虑当前枚举的行是最高行、在它的管辖区间内、且跨过它的矩形。这样做去重问题得到解决。
考虑计算答案。设
我们现在要在
引入一个
那么有
然后你会发现
::::success[码]
#include<bits/stdc++.h>
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define il inline
using namespace std;
char buf[1<<20],*p1=buf,*p2=buf;
il int read(){
int x=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return x;
}
using ll=long long;
constexpr int MAXN=2005;
int n,m,K,a[MAXN][MAXN],b[MAXN];
int stk[MAXN],L[MAXN],R[MAXN],top;
ll g[MAXN][MAXN],h[MAXN][MAXN],ans;
il void init(){
for(int i=1;i<=m;i++)
for(int j=i;j<=m;j++)
h[i][j]=h[i][j-1]+j-i+1;
for(int j=1;j<=m;j++){
ll sm=0;
for(int i=1;i<=n;i++){
sm+=h[min((K+i-1)/i,m+1)][j];
g[i][j]=sm;
}
}
}
int main(){
n=read(),m=read(),K=read();
init();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read();
fill(b+1,b+m+1,n+1);
for(int i=n;i;i--){
for(int j=1;j<=m;j++)
if(a[i][j])
b[j]=i;
stk[top=0]=0;
for(int j=1;j<=m;j++){
while(top&&b[stk[top]]>=b[j]) top--;
L[j]=stk[top]+1;
stk[++top]=j;
}
stk[top=0]=m+1;
for(int j=m;j;j--){
while(top&&b[stk[top]]>b[j]) top--;
R[j]=stk[top]-1;
stk[++top]=j;
}
for(int j=1;j<=m;j++)
if(b[j]!=i)
ans+=g[b[j]-i][R[j]-L[j]+1]-g[b[j]-i][j-L[j]]-g[b[j]-i][R[j]-j];
}
printf("%lld\n",ans);
return 0;
}
::::
再介绍另一种比较类人的
实际上,这个做法在常数优秀、块长合理的情况下,可以稳稳通过。
背后的故事:机房某骗分战神用正难则反+珂朵莉树在比赛时 A 了这道题,但是无法通过 luogu 数据。
感觉
计算矩形数量可以用单调栈简单计算,这里重点讲
考虑枚举右下角
所以,我们现在需要一种 DS,可以实现前缀加一,后缀归零,以及在每一次操作后,数组整体对
发现这个东西是处理困难的,考虑大力分块。
由于枚举顺序是一行一行枚举的,所以
由于有覆盖操作,可以只保留整块
覆盖时分散块归零和整块归零。散块归零暴力重构整个
整块归零时,考虑给每个块设
设块长为
::::success[码(原作者)]
#include<bits/stdc++.h>
using namespace std;
char buf[1<<20],*p1,*p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
template<typename T>
inline void read(T &x){
bool f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=(f?x:-x);
}
template<typename T>
inline void write(T x){
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=2005,bl=20,M=105;
int n,m,k,pr[N],nx[N],ls[N],st[N],tp;
int b[N],num[N],tag[M],sm[M],c[M][N],gx[M],vs[M],snm[M],mx,sum;
long long ans;
inline void ret(int x,int cc=0){
if(!vs[x]&&!cc){
sum-=gx[x];
tag[x]=gx[x]=0;
sm[x]=c[x][0];
return;
}
int rs=x*bl,ls=rs-bl+1;
rs=min(rs,mx),sum-=gx[x];
gx[x]=tag[x]=vs[x]=0;
while(ls<=rs){
c[x][num[ls]]--;
c[x][num[ls]=b[ls]]++,ls++;
}
if(cc&&rs==mx) c[x][0]++;
sm[x]=c[x][0];
}
inline void chg(int x){
for(int i=1;i*bl-bl<x;i++)
sum+=snm[i]-sm[i],gx[i]+=snm[i]-sm[i],sm[i]+=c[i][++tag[i]];
int bid=x/bl+1,rs=min(bid*bl,mx);
sum-=gx[bid],gx[bid]=0;
for(int i=bid*bl-bl+1;i<=x;i++){
c[bid][num[i]]--,num[i]=max(num[i]-tag[bid],0);
c[bid][num[i]]++,gx[bid]+=b[i]-num[i];
}
for(int i=rs;i>x;i--) c[bid][num[i]]--,c[bid][num[i]=b[i]]++;
tag[bid]=0,sum+=gx[bid];
sm[bid]=c[bid][0],vs[bid]=1;
for(int i=bid+1;i*bl-bl<mx;i++) ret(i);
}
int main(){
read(n),read(m),read(k);
for(int i=1;i<=n;i++,tp=0){
for(int j=1,cc;j<=m;j++){
read(cc);
(cc?pr[j]=0:pr[j]++);
while(tp&&pr[st[tp]]>pr[j]) nx[st[tp--]]=j;
ls[j]=st[tp],nx[j]=m+1,st[++tp]=j;
}
mx=i;
for(int j=1;j<=m;j++) ans+=1ll*pr[j]*(j-ls[j])*(nx[j]-j);
for(int j=1;j<=i;j++) b[i]=min((k-1)/j,2001);
for(int j=1;j*bl-bl<i;j++) ret(j,1),snm[j]=min(j*bl,mx)-j*bl+bl;
for(int j=1;j<=m;j++) chg(pr[j]),ans-=sum;
}
write(ans);
return 0;
}
::::