zoukankan      html  css  js  c++  java
  • 洛谷 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;
    } 
    
  • 相关阅读:
    算数表达式二叉树
    Java汉诺塔算法
    Struts2中的设计模式ThreadLocal模式续
    Java基础知识总结(五)
    Java数组扩容算法及Java对它的应用
    Java Arrays.sort源代码解析
    Java字符串排列算法
    Java基础知识总结(三)
    SSIS OLE DB Source中执行带参数的存储过程
    Sql server中Collation conflict问题
  • 原文地址:https://www.cnblogs.com/mysterious-garden/p/9751177.html
Copyright © 2011-2022 走看看