[loj3461]Paint by Letters

将方格(参考题目)看作一个点,相邻的两点若颜色相同则连边,即得到一张网格图,而每次询问即求子矩形内的点导出子图对应的连通块数

注意到网格图也是平面图,根据欧拉公式有$V+F-E=1+C$(其中$V,F,E$和$C$分别为点数、块数、边数和连通块数),那么不妨去求$V,F$和$E$

显然$V=(x_{2}-x_{1}+1)(y_{2}-y_{1}+1),E$可以通过二维差分$o(1)$求出($o(nm)$预处理)

关于$F$,将原网格图中的格子看作一个点,相邻两个格子间有边当且仅当对应的公共边在原图中不存在,由此即得到一张$(n-1)(m-1)$个点的新网格图

此时的问题类似于求$C$,但不需要考虑与矩形外有边的连通块,因此可以计算

具体的,记原图中$(x,y),(x,y+1),(x+1,y)$和$(x+1,y+1)$为顶点的方格在新图中对应的点为$(x,y)$,那么问题所求的即是新图中全部在$x\in [x_{1},x_{2}),y\in [y_{1},y_{2})$这个范围内的连通块数+1

(注意到不严格在其内部的,必然会与外界成为同一个块)

可以对每一个连通块求出包含其的极小矩形,但之后的问题并不容易处理

在每一个连通块中任选一个点,显然连通块全部在该矩形内部的必要条件为该点在矩形内部

由此,同样二维差分求出这样的连通块数,再枚举矩形更外面一圈,若该点所在的连通块对应的点在矩形内部则将其删除(重复只删除一次)

时间复杂度为$o(nm+qn+qm)$,可以通过

[loj3461]Paint by Letters
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 1005
 4 #define fi first
 5 #define se second
 6 #define y1 y11
 7 struct Edge{
 8     int nex,to;
 9 }edge[4*N*N];
10 pair<int,int>pos[N*N];
11 int n,m,q,V,E,F,scc,x1,y1,x2,y2,sumx[N][N],sumy[N][N],sumf[N][N],head[N*N],bl[N*N],vis[N*N];
12 char s[N][N];
13 int id(int x,int y){
14     if ((!x)||(!y)||(x==n)||(y==m))return 0;
15     return (x-1)*(m-1)+y;
16 }
17 void add(int x,int y){
18     edge[E].nex=head[x];
19     edge[E].to=y;
20     head[x]=E++;
21 }
22 void dfs(int k){
23     if (bl[k])return;
24     bl[k]=scc;
25     for(int i=head[k];i!=-1;i=edge[i].nex)dfs(edge[i].to);
26 }
27 int main(){
28     scanf("%d%d%d",&n,&m,&q);
29     for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
30     for(int i=2;i<=n;i++)
31         for(int j=1;j<=m;j++)
32             sumx[i][j]=sumx[i-1][j]+sumx[i][j-1]-sumx[i-1][j-1]+(s[i][j]==s[i-1][j]);
33     for(int i=1;i<=n;i++)
34         for(int j=2;j<=m;j++)
35             sumy[i][j]=sumy[i-1][j]+sumy[i][j-1]-sumy[i-1][j-1]+(s[i][j]==s[i][j-1]);
36     memset(head,-1,sizeof(head));
37     for(int i=0;i<n;i++)
38         for(int j=0;j<m;j++){
39             if ((j)&&(s[i+1][j]!=s[i+1][j+1])){
40                 add(id(i,j),id(i+1,j));
41                 add(id(i+1,j),id(i,j));
42             }
43             if ((i)&&(s[i][j+1]!=s[i+1][j+1])){
44                 add(id(i,j),id(i,j+1));
45                 add(id(i,j+1),id(i,j));
46             }
47         }
48     for(int i=0;i<n;i++)
49         for(int j=0;j<m;j++)
50             if (!bl[id(i,j)]){
51                 pos[++scc]=make_pair(i,j);
52                 dfs(id(i,j));
53             }
54     for(int i=1;i<=scc;i++)sumf[pos[i].fi][pos[i].se]=1;
55     sumf[0][0]=0;
56     for(int i=1;i<n;i++)
57         for(int j=1;j<m;j++)sumf[i][j]+=sumf[i-1][j]+sumf[i][j-1]-sumf[i-1][j-1];
58     for(int i=1;i<=q;i++){
59         scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
60         V=(x2-x1+1)*(y2-y1+1),E=0;
61         E+=sumx[x2][y2]-sumx[x1][y2]-sumx[x2][y1-1]+sumx[x1][y1-1];
62         E+=sumy[x2][y2]-sumy[x2][y1]-sumy[x1-1][y2]+sumy[x1-1][y1];
63         x2--,y2--;
64         F=sumf[x2][y2]-sumf[x1-1][y2]-sumf[x2][y1-1]+sumf[x1-1][y1-1];
65         for(int j=y1;j<=y2;j++){
66             int k=bl[id(x1-1,j)];
67             if ((x1<=pos[k].fi)&&(pos[k].fi<=x2)&&(y1<=pos[k].se)&&(pos[k].se<=y2)&&(vis[k]!=i)){
68                 F--;
69                 vis[k]=i;
70             }
71             k=bl[id(x2+1,j)];
72             if ((x1<=pos[k].fi)&&(pos[k].fi<=x2)&&(y1<=pos[k].se)&&(pos[k].se<=y2)&&(vis[k]!=i)){
73                 F--;
74                 vis[k]=i;
75             }
76         }
77         for(int j=x1;j<=x2;j++){
78             int k=bl[id(j,y1-1)];
79             if ((x1<=pos[k].fi)&&(pos[k].fi<=x2)&&(y1<=pos[k].se)&&(pos[k].se<=y2)&&(vis[k]!=i)){
80                 F--;
81                 vis[k]=i;
82             }
83             k=bl[id(j,y2+1)];
84             if ((x1<=pos[k].fi)&&(pos[k].fi<=x2)&&(y1<=pos[k].se)&&(pos[k].se<=y2)&&(vis[k]!=i)){
85                 F--;
86                 vis[k]=i;
87             }
88         }
89         printf("%d\n",V+F-E);
90     }
91     return 0;
92 }
View Code

 

[loj3461]Paint by Letters

上一篇:windows 微信多开方法 .bat方法


下一篇:mq-deadline调度器原理及源码分析