题目链接

题意:给你一个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. English trip -- VC(情景课)1 A Get ready

    Meet your classmates 见见你的同学 Look at the picture. What do you see? 看图片.你看到了什么? calendar  日历 bookcase ...

  2. Confluence 6 的 Crowd 权限

    只读(Read Only) 从 Crowd 上获取的用户,用户组和用户组成员信息只具有读取权限,你只能在 Crowd 上对你的配置进行修改.你不能通过你的应用程序管理员界面修改,用户,用户组,用足成员 ...

  3. DZY Loves Colors CodeForces - 444C (线段树势能分析)

    大意:有$n$个格子, 初始$i$位置的颜色为$i$, 美丽值为0, 有两种操作 将区间$[l,r]$内的元素全部改为$x$, 每个元素的美丽值增加$|x-y|$, $y$为未改动时的值 询问区间$[ ...

  4. CentOS查看分区的方式

    看ls /dev然后挂载 df -T  只可以查看已经挂载的分区和文件系统类型 fdisk -l 可以显示出所有挂载和未挂载的分区,但不显示文件系统类型 parted -l 可以查看未挂载的文件系统类 ...

  5. 常用Java字符API

  6. hdu-2147-博弈

    kiki's game Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 40000/10000 K (Java/Others)Total ...

  7. java 中利用异或实现两个变量互换

    一般实现两个变量之间的互换要用第三个变量,这样做可以,但创建新变量,增加了系统开销.如果要交换的变量时两个整数型变量,可以用更高效的方法.例如:^(异或)操作,举例如下: package chapte ...

  8. 使用pthread_create()创建线程

    可以通过 pthread_create()函数创建新线程. #include <pthread.h> int pthread_create(pthread_t *restrict tidp ...

  9. duilib CEditUI 禁止输入中文字符,禁止复制粘贴

    1.CEditUI 禁止使用中文输入法 在 CEditUI::DoEvent 函数中,添加代码: if(m_bOnlyEnglishChar && m_pWindow &&am ...

  10. cas Cas20ProxyReceivingTicketValidationFilter

    Cas20ProxyReceivingTicketValidationFilter  继承AbstractTicketValidationFilter,这里有几个模板方法.例如getTicketVal ...