正题

题目链接:https://www.luogu.com.cn/problem/P7295


题目大意

给出\(n*m\)的网格,每个格子上有字母,相同字母的四联通相邻格子为连通,每次询问一个子矩阵求连通块个数。

\(1\leq n,m,q\leq 1000\)


解题思路

首先一张连通的平面图有欧拉公式

\[V+F=E+2
\]

其中\(V,E,F\)分别表示点数,边数,区域个数(对偶图点数)。

然后不连通的对偶图会共用一个无界域,设为\(C\)个连通块,无界域会重复统计\(C-1\)次,然后联立得

\[V+F-E=C+1
\]

然后考虑怎么用这个求,首先是\(V,E\),这个很容易搞,\(V\)直接计算,\(E\)用二维前缀和算就好了。

主要是\(F\)怎么搞,先构出不严格的对偶图(就是每个格子边上的点当做点),然后\(F\)就是对偶图的连通块数。

对于整张图的每个连通块,我们选择任意一个点标记,然后记录每个点对应连通块的标记点,然后直接二维前缀和统计连通块内的标记点个数,然后枚举边界减去边上不完整被统计的的连通块最后加上无界域就好了。

时间复杂度\(O(nm+q(n+m))\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1100;
const int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int n,m,q,E[2][N][N],mx[N][N],my[N][N],F[N][N];
char s[N][N];bool v[N][N];
bool edg(int x,int y,int zx,int zy){
if(zx<0||zy<0||zx>n||zy>m)return 0;
if(zx>0&&zx<n&&zy==y+1)return (s[x][y+1]!=s[x+1][y+1]);
if(zx>0&&zx<n&&zy==y-1)return (s[x][y]!=s[x+1][y]);
if(zy>0&&zy<m&&zx==x+1)return (s[x+1][y]!=s[x+1][y+1]);
if(zy>0&&zy<m&&zx==x-1)return (s[x][y]!=s[x][y+1]);
return 1;
}
void dfs(int x,int y){
if(x==1&&y==4)
x++,x--;
if(v[x][y])return;v[x][y]=1;
for(int k=0;k<4;k++){
int zx=x+dx[k],zy=y+dy[k];
if(edg(x,y,zx,zy)){
mx[zx][zy]=mx[x][y];
my[zx][zy]=my[x][y];
dfs(zx,zy);
}
}
return;
}
#define Get(F,x1,y1,x2,y2) (F[x2][y2]-((x1)?F[x1-1][y2]:0)-((y1)?F[x2][y1-1]:0)+(((x1)&&(y1))?F[x1-1][y1-1]:0))
int check(int x,int y,int x1,int y1,int x2,int y2){
if(!v[x][y]&&x>=x1&&x<=x2&&y>=y1&&y<=y2)
{v[x][y]=1;return 1;}
return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
E[0][i][j]=E[0][i-1][j]+E[0][i][j-1]-E[0][i-1][j-1];
E[1][i][j]=E[1][i-1][j]+E[1][i][j-1]-E[1][i-1][j-1];
if(s[i][j]==s[i][j+1])E[0][i][j]++;
if(s[i][j]==s[i+1][j])E[1][i][j]++;
}
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
if(!v[i][j]){
mx[i][j]=i;my[i][j]=j;
F[i][j]++;dfs(i,j);
}
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
F[i][j]+=(i?F[i-1][j]:0)+(j?F[i][j-1]:0)-((i&&j)?F[i-1][j-1]:0);
memset(v,0,sizeof(v));
while(q--){
int x1,y1,x2,y2,ans=0;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
ans+=(x2-x1+1)*(y2-y1+1);
if(y1!=y2)ans-=Get(E[0],x1,y1,x2,y2-1);
if(x1!=x2)ans-=Get(E[1],x1,y1,x2-1,y2);
x1--;y1--;ans+=Get(F,x1,y1,x2,y2);
for(int i=x1;i<=x2;i++){
ans-=check(mx[i][y1],my[i][y1],x1,y1,x2,y2);
ans-=check(mx[i][y2],my[i][y2],x1,y1,x2,y2);
}
for(int i=y1;i<=y2;i++){
ans-=check(mx[x1][i],my[x1][i],x1,y1,x2,y2);
ans-=check(mx[x2][i],my[x2][i],x1,y1,x2,y2);
}
printf("%d\n",ans);
for(int i=x1;i<=x2;i++){
v[mx[i][y1]][my[i][y1]]=0;
v[mx[i][y2]][my[i][y2]]=0;
}
for(int i=y1;i<=y2;i++){
v[mx[x1][i]][my[x1][i]]=0;
v[mx[x2][i]][my[x2][i]]=0;
}
}
return 0;
}

P7295-[USACO21JAN]Paint by Letters P【平面图欧拉公式】的更多相关文章

  1. ZOJ 2589 Circles(平面图欧拉公式)

    [题目链接] http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2589 [题目大意] 给出一些圆,问这些圆可以把平面分为几个部 ...

  2. POJ--2284--That Nice Euler Circuit【平面图欧拉公式】

    链接:id=2284">http://poj.org/problem?id=2284 题意:一个自己主动绘图的机器在纸上(无限大)绘图,笔尖从不离开纸,有n个指令,每一个指令是一个坐标 ...

  3. android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索

    我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体 ...

  4. Android ListView A~Z快速索引(改进版)

    上一篇文章虽然实现了ListView 快速索引的效果,但是有一个小小的Bug.这个Bug我在前面也说了,这篇文章就来解决这个Bug. 我研究的时候发现只要showBg值为true,中间的字母就显示,而 ...

  5. 实现ListView A~Z快速索引

    ListView A~Z快速索引这种效果在通信录和城市列表中经常看到,方便用户查找,是一种增加用户体验的好方法. 实现步骤: 1.自定义一个名叫SlideBar 的View. 2.在布局文件中加入这个 ...

  6. Android通讯录管理(获取联系人、通话记录、短信消息)

    前言:前阵子主要是记录了如何对联系人的一些操作,比如搜索,全选.反选和删除等在实际开发中可能需要实现的功能,本篇博客是小巫从一个别人开源的一个项目抽取出来的部分内容,把它给简化出来,可以让需要的朋友清 ...

  7. ZOJ 3781 Paint the Grid Reloaded(BFS)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781 Leo has a grid with N rows an ...

  8. poj2284 That Nice Euler Circuit(欧拉公式)

    题目链接:poj2284 That Nice Euler Circuit 欧拉公式:如果G是一个阶为n,边数为m且含有r个区域的连通平面图,则有恒等式:n-m+r=2. 欧拉公式的推广: 对于具有k( ...

  9. POJ2284 That Nice Euler Circuit (欧拉公式)(计算几何 线段相交问题)

                                                          That Nice Euler Circuit Time Limit: 3000MS   M ...

随机推荐

  1. 【AI】Pytorch_预训练模型

    1. 模型下载 import re import os import glob import torch from torch.hub import download_url_to_file from ...

  2. swiper在一个页面多个轮播图

    <script> var swiper = new Swiper('.swiper-container1', { spaceBetween: 30, centeredSlides: tru ...

  3. springboot整合多数据源解决分布式事务

    一.前言        springboot整合多数据源解决分布式事务.             1.多数据源采用分包策略              2.全局分布式事务管理:jta-atomikos. ...

  4. linux 下的用户的管理

  5. 未解决:为什么在struts2下新建ognl的包,会出错?

    首先开始在src下新建了一个名叫ognl的包: 发现在其中放置了一个loginAction,即使是最简单的跳转都不能实现: 直接抛出了java.lang.Exception; 传递参数更出现了异常: ...

  6. spring动态切换数据源(一)

    介绍下spring数据源连接的源码类:| 1 spring动态切换连接池需要类AbstractRoutingDataSource的源码 2 /* 3 * Copyright 2002-2017 the ...

  7. Django——后台管理

    1.要使用Django-admin后台的前提 INSTALLED_APPS = [ 'simpleui', 'django.contrib.admin', #必须有这一项 'django.contri ...

  8. Appium问题解决方案(6)- Java堆栈错误:java.lag.ClassNotFoundException:org.eclipse.swt.widets.Control

    背景 运行脚本出现 SWT folder '..\lib\location of your Java installation.' does not exist. Please set ANDROID ...

  9. Mysql常用sql语句(11)- between and 范围查询

    测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 between and可以判断值是否在指定范围内 ...

  10. c# 递归树形菜单

    首先创建模型类Menus public class Menus { //菜单Id public int Id { get; set; } //菜单名 public string MenuName { ...