正题
题目链接:https://www.luogu.com.cn/problem/CF1598E
题目大意
给出一个\(n\times m\)的网格图,开始所有都是黑色的,\(q\)次取反一个格子的颜色,然后求楼梯的数量。
楼梯定义为全黑色的下/右交替的格子集。
\(1\leq n,m\leq 1000,1\leq q\leq 10^4\)
解题思路
注意到其实是两个斜行交错,可以考虑把坐标轴旋转\(45°\),然后发现其实就是相邻的两行的正方形数量。
\(f_{i,j}\)表示格子\((i,j)\)所在斜行再往\((i,j)\)左上角的能延伸多少个黑色,然后每次\(O(n)\)暴力修改即可。
时间复杂度:\(O(qn)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1100;
ll n,m,q,w[N][N],a[N][N],ans;
ll calc(ll x,ll y)
{
if(y<0)return 0;
return min(x,y+1)+min(x,y);
}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&q);
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
w[i][j]=w[i-1][j-1]+1;
for(ll i=1;i<=n;i++)
for(ll j=1;j<=m;j++)
ans+=calc(w[i][j],w[i-1][j])+calc(w[i][j],w[i][j-1])-1;
while(q--){
ll x,y;
scanf("%lld%lld",&x,&y);
if(a[x][y]){
ll dx=x,dy=y;x++;y++;
while(x<=n&&y<=m&&!a[x][y]){
ans-=calc(w[x][y],w[x-1][y])+calc(w[x][y],w[x][y-1])-1;
ans-=calc(w[x][y],w[x+1][y]-1)+calc(w[x][y],w[x][y+1]-1);
x++;y++;
}
x=dx;y=dy;a[x][y]^=1;
while(x<=n&&y<=m&&!a[x][y])
w[x][y]=w[x-1][y-1]+1,x++,y++;
x=dx;y=dy;
while(x<=n&&y<=m&&!a[x][y]){
ans+=calc(w[x][y],w[x-1][y])+calc(w[x][y],w[x][y-1])-1;
ans+=calc(w[x][y],w[x+1][y]-1)+calc(w[x][y],w[x][y+1]-1);
x++;y++;
}
}
else{
ll dx=x,dy=y;
while(x<=n&&y<=m&&!a[x][y]){
ans-=calc(w[x][y],w[x-1][y])+calc(w[x][y],w[x][y-1])-1;
ans-=calc(w[x][y],w[x+1][y]-1)+calc(w[x][y],w[x][y+1]-1);
x++;y++;
}
x=dx;y=dy;a[x][y]^=1;w[x][y]=0;x++;y++;
while(x<=n&&y<=m&&!a[x][y])
w[x][y]=w[x-1][y-1]+1,x++,y++;
x=dx;y=dy;x++;y++;
while(x<=n&&y<=m&&!a[x][y]){
ans+=calc(w[x][y],w[x-1][y])+calc(w[x][y],w[x][y-1])-1;
ans+=calc(w[x][y],w[x+1][y]-1)+calc(w[x][y],w[x][y+1]-1);
x++;y++;
}
}
printf("%lld\n",ans);
}
}