洛谷 AT2167 Blackout 题解

传送门

题目描述

我们有一个(N)(N)列的矩阵。第(i)行第(j)列的格子表示为((i,j))

开始时,有(M)个格子是黑色,其他格子都是白色。特别地,开始时格子((a1,b1),(a2,b2),...,(am,bm))是黑色。

スヌケ君会按照以下的规则尽可能多的将白色格子涂成黑色:

  • 对于整数(1≤x,y,z≤N),如果((x,y))((y,z))都是黑色,那么就把((z,x))涂黑。

请计算出当再也没有白色格子能被涂黑时,黑色格子的个数。

说明

  • (1≤N≤10^5)
  • (1≤M≤10^5)
  • (1≤ai,bi≤N)
  • 各黑格坐标互不相同

思路

对于这样一个迷之题面大家第一反应大概是神题不可做,然而显然是可以做的,我们对每一个黑色方块((x,y))建立一条有向边,边权记为(1),为了方便后续对这张图的遍历、染色工作,我们再建立一条反向的((y,x)),不过为了与描述黑色方块的边区分开来,将边权标记为(-1).

考虑图中的每一个联通块,我们对其进行三染色操作,基本染色原则为(r) -> (g) -> (b) -> (r).

染色完毕后,我们不难发现以下规律:

  • 对于每个无法成功染色的联通块(颜色相互冲突),根据题意,我们都可以构造一张可以含有自环的完全图(注意:根据题意,形如((x,x))的点是可行的).显然,这样的结构对答案的贡献为(n^2).

  • 对于每个可以成功染色的联通块,如果三种颜色均包含在内,那么根据题意,该联通块中每一对(r) -> (g)(g) -> (b)(b) -> (r)均可以连出一条有向边(暨染成黑色).若约定用(n(r))表示该种颜色的节点在联通块中的数量,这样的结构对答案的贡献可以表示为(n(r)*n(g) + n(g) * n(b) + n(b) * n(r)).

  • 对于每个可以成功染色的联通块,如果仅有两种或更少的颜色包含在内,那么显然我们无法染出更多的黑块.则这样的结构对答案没有额外贡献.

根据如上三条规律,我们不难写出如下程序。不过具体地,一个小小的细节是由于答案可能过大,我们需要用(long long)型变量对答案进行储存。并且,我们要时刻注意(int)乘法中可能存在的溢出问题,解决方案很简单,在每一个(int)乘法运算前加上((long long))即可。

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
inline int read(){
	int re = 0,ch = getchar();
	while(!isdigit(ch))ch = getchar();
	while(isdigit(ch))re = (re<<1)+(re<<3)+ch-'0',ch = getchar();
	return re;
}
const int maxn = 200010;
struct edge{
	int v,w,nxt;
}e[maxn];
int h[maxn],cnt;
inline void add(int u,int v,int w){
	e[++cnt].v = v;
	e[cnt].w = w;
	e[cnt].nxt = h[u];
	h[u] = cnt;
}
inline int color(int c){
	if(c >= 3)c -= 3;
	if(c < 0)c += 3;
	return c;
}
int col[maxn],cnum[3],nsz,esz;
bool flag = 1,vis[maxn];
void coloring(int u){
	cnum[col[u]]++;nsz++;vis[u] = 1;
	for(int i = h[u];i;i = e[i].nxt){
		int v = e[i].v;
		if(e[i].w == 1)esz++;
		if(!vis[v]) col[v] = color(col[u] + e[i].w),coloring(v);
		else if(col[v] != color(col[u] + e[i].w)) flag = 0;
	}
}
int n,m;
long long ans;
int main(){
	n = read(),m = read();
	int u,v;
	for(int i = 1;i <= m;i++){
		u = read(),v = read();
		add(u,v,1);
		add(v,u,-1);
	}
	for(int i = 1;i <= n;i++){
		if(vis[i])continue;
		memset(cnum,0,sizeof(cnum));
		nsz = 0,esz = 0,flag = 1;
		coloring(i);
		if(!flag) ans += (long long)nsz * nsz;
		else if(cnum[0] && cnum[1] && cnum[2]) 
			ans += (long long)cnum[0] * cnum[1]
			 + (long long)cnum[1] * cnum[2] + (long long)cnum[2] * cnum[0];
		else ans += esz;
	}
	printf("%lld",ans);
	return 0;
} 
原文地址:https://www.cnblogs.com/mysterious-garden/p/9751177.html