题目链接

题意:给你一个n*m的网格图,初始时格点全白,每次可以将一段连续的格点涂黑。求出每次操作之后白色连通块的数量。

看了看网上的题解,基本全是离线的做法。其实这道题是有在线的做法的,利用了对偶图的性质,适用于任意平面图(大概是)。

(ps:本题思路是我受thu叉院神犇wzf在wannafly冬令营上提到的对偶图思想的启发而想出来的,先膜为敬~~)

我们可以反过来考虑黑格的连通性。假如我们在涂黑某个格点的时候,把两个分离的黑格“连了起来”,这时有两种可能的情况:

1.两黑格在同一连通分量。此时如果将两黑格连接,必将形成一个回路,并且产生一个新的白色连通块。(回路中包着的区域就是一个白色连通块)

2.两黑格不在同一连通分量。此时将两黑格连接不会形成回路,因而也不会产生新的白色连通块。

注意:

1.白格是四连通的,因此对偶图中的黑格应当是八连通的。

2.上面的“连通分量”指的是外侧的连通分量,而不是内侧的连通分量。

例如,假如我们面临下面的状况:

此时,黑格3,4,5已经相连,因此需要把它们看成一个整体,而1,2各自分离,需要单独考虑。我们试图将中间的白色格子涂黑,假设1,2,3,4,5在外侧区域同属于一个连通块,那么中间的白色连通块数量将由原来的一个拆分成3个,分别夹在(1,2),(1,3)和(2,5)之间。

因此我们可以总结出规律:把与中间格点相邻的8个格子中所有的黑格按照顺时针或者逆时针顺序排成一列,把相互八连通的格子放在同一个内侧连通分量中(这个过程可以用另一个并查集实现)。我们把内侧连通分量不同的两个相邻格子两两合并,如果两个格子恰好在外侧同属于一个连通分量,那么必将多出一个“包起来”的白色连通块,此时答案+1。

注意将最后一个连通分量与第一个连通分量合并时不要计算对答案的贡献,因为合并后,原来中间的白色连通块就“消失”了,而它不应该消失,为了不让它消失,需要把这个连通块归结到一个未合并的连通区域中,因此需要保留一个白色连通区域作为原来中间白色连通块的新的“归属”。

最后需要注意的就是如果中间的白格的上下左右四个方向都是黑格,此时中间的白格单独属于一个连通块,在涂黑之后不能归结到任何一个区域,直接就消失了,此时答案应当-1。

还有就是在初始时应当把网格的四周涂黑,简化判断。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=+;
const int dx[]= {-,,,,,,-,-};
const int dy[]= {-,-,-,,,,,};
int n,m,nq,fa[N*N],qx[],qy[],fa2[],n2,ans,a[N][N];
int fd(int x,int* fa) {return ~fa[x]?fa[x]=fd(fa[x],fa):x;}
bool mg(int x,int y,int* fa) {
int fx=fd(x,fa),fy=fd(y,fa);
if(fx==fy)return ;
else {fa[fx]=fy; return ;}
}
bool adj(int x1,int y1,int x2,int y2) {//判断是否8连通
return abs(x1-x2)+abs(y1-y2)==||(abs(x1-x2)==&&abs(y1-y2)==);
}
int id(int x,int y) {return x*(m+)+y;}//给格点标号
void solve(int x,int y) {
if(a[x][y])return;
a[x][y]=,n2=;
for(int i=; i<; ++i) {
int xx=x+dx[i],yy=y+dy[i];
if(a[xx][yy])qx[n2]=xx,qy[n2++]=yy;
}//把周围相邻的黑格按顺序扒出来
for(int i=; i<n2; ++i)fa2[i]=-;
for(int i=; i<n2; ++i)
if(adj(qx[i],qy[i],qx[(i+)%n2],qy[(i+)%n2]))mg(i,(i+)%n2,fa2);//合并内侧相邻黑格
for(int i=; i<n2-; ++i)
if(fd(i,fa2)!=fd(i+,fa2)&&fd(i+,fa2)!=fd(,fa2)
&&!mg(id(qx[i],qy[i]),id(qx[i+],qy[i+]),fa))++ans;//如果内侧不连通的黑格在外侧连通,则答案+1
if(a[x][y-]&&a[x][y+]&&a[x-][y]&&a[x+][y])--ans;//中间的格子被包围,答案-1
if(n2)mg(id(qx[],qy[]),id(x,y),fa);//将周围的黑格与中间的格子合并
} int main() {
scanf("%d%d%d",&n,&m,&nq);
memset(fa,-,sizeof fa);
for(int i=; i<=n+; ++i)a[i][]=a[i][m+]=,fa[id(i,)]=fa[id(i,m+)]=;
for(int i=; i<=m+; ++i)a[][i]=a[n+][i]=,fa[id(,i)]=fa[id(n+,i)]=;//将边界格子涂黑并放在同一个连通块
fa[]=-,ans=;
while(nq--) {
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
if(x1<x2) for(int i=x1; i<=x2; ++i)solve(i,y1);
else for(int i=y1; i<=y2; ++i)solve(x1,i);
printf("%d\n",ans);
}
return ;
}

Gym - 101550A Artwork (并查集在线做法)的更多相关文章

  1. Artwork Gym - 101550A 离线并查集

    题目:题目链接 思路:每个空白区域当作一个并查集,因为正着使用并查集分割的话dfs会爆栈,判断过于复杂也会导致超时,我们采用离线反向操作,先全部涂好,然后把黑格子逐步涂白,我们把每个空白区域当作一个并 ...

  2. Codeforces Gym 100463E Spies 并查集

    Spies Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100463/attachments Desc ...

  3. Gym - 100676F Palindrome —— 并查集

    题目链接:https://vjudge.net/contest/155789#problem/E 题解: 由于是回文串,所以可以先将在对称位置的字符放在同一个集合(如果期间有两个非‘?’,且不相等,则 ...

  4. POJ1703 && POJ2942 &&POJ 1182 并查集 这个做法挺巧妙

    Find them, Catch them Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 37242   Accepted: ...

  5. 2019-2020 ACM-ICPC Brazil Subregional Programming Contest Problem A Artwork (并查集)

    题意:有一个矩形,有\(k\)个警报器,警报器所在半径\(r\)内不能走,问是否能从左上角走到右下角. 题解:用并查集将所有相交的圆合并,那么不能走的情况如下图所示 所以最后查询判断一下即可. 代码: ...

  6. UVALive 6910 Cutting Tree(并查集应用)

    总体来说,这个题给的时间比较长,样例也是比较弱的,别的方法也能做出来. 我第一次使用的是不合并路径的并查集,几乎是一种暴力,花了600多MS,感觉还是不太好的,发现AC的人很多都在300MS之内的过得 ...

  7. 还是畅通工程(hdu1233)并查集应用

    还是畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

  8. 【bzoj4998】星球联盟(并查集+边双)

    题面 传送门 题解 总算有自己的\(bzoj\)账号啦! 话说这题好像\(Scape\)去年暑假就讲过--然而我到现在才会-- \(LCT\)什么的跑得太慢了而且我也不会,所以这里是一个并查集的做法 ...

  9. BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题意 一开始串为空,每次往串后面加一个字符,求本质不同的子串的个数,可以离线.即长度为 ...

随机推荐

  1. C++编程模板2

    C++编程模板2 #include <iostream> using namespace std; /* */ int main(){ int ans; printf("%d\n ...

  2. SSH Secure Shell Client--- the host may be dow

    the host may be down,or there may be a problem with the network connection. Sometimes such problems ...

  3. XSD与C#Code以及XML之间的相互关心

    ------------------------------网上参考资料 C# 利用自带xsd.exe工具操作XML-如通过XML生成xsd文件:http://blog.sina.com.cn/s/b ...

  4. LeetCode--175--组合两个表

    问题描述: 表1: Person +-------------+---------+ | 列名 | 类型 | +-------------+---------+ | PersonId | int | ...

  5. 20170728xlVba简单的匹配

    Sub MatchData() Dim i As Long, EndRow As Long, Key As String Dim Rng As Range Dim Dic As Object Set ...

  6. php--------获取当前时间、时间戳

    首先需要知道的是在php中获取时间方法是date(),在php中获取时间戳方法有time().strtotime().下面分别说明. date() 格式为:date($format, $timesta ...

  7. AND Graph CodeForces - 987F (状压)

    链接 大意:给定$m$个数, 若$x\&y=0$, 则在$x$与$y$之间连一条无向边. 求无向图的连通块个数 暴力连边显然超时的, 可以通过辅助结点优化连边, 复杂度$O(n2^n)$ #i ...

  8. Linux中su、su -和sudo的区别

    su 切换到root用户,但是并没有转到root用户家目录下,即没有改变用户的环境. su - 切换到root用户,并转到root用户的家目录下,即改变到了root用户的环境. 这个涉及到不同用户下的 ...

  9. Python基础--文件操作和集合

    这篇博客来说一下python对文件的操作. 对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句柄操作文件 3.关闭文件. 现有以下文件file.txt: 我们哭了 ...

  10. cf-789A (思维)

    A. Anastasia and pebbles time limit per test 1 second memory limit per test 256 megabytes input stan ...