CF1598E-Staircases【计数】

正题

题目链接: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);
	}
}
posted @ 2021-10-24 21:35  QuantAsk  阅读(55)  评论(0编辑  收藏  举报