• agc006_f Blackout


    agc006_f

    题目叙述

    给一个有向图,对于三个点如果 (u ightarrow v)(v ightarrow w)(u,v,w) 不一定两两不同),那么再连一条 (w ightarrow u) 的边。求最终的总边数。图可能有自环。

    题解

    对每个弱连通块分别考虑。弱连通块定义为将有向边视为无向边的连通块。

    将图用三种颜色红、黄、蓝染色。红色点只能向黄色点连边,黄色点只能向蓝色点连边,蓝色点只能向红色点连边。弱环定义为将所有边视为无向图形成的环。

    如果不存在一个这样的染色方法,设块的大小为 (x),那么将产生 (x^2) 条边。因为这样必然会有一个弱环满足他不能被染色。现在每次将这个弱环的边 (u ightarrow v)(v ightarrow w) 的边缩为 (w ightarrow u) ,那么这个弱环必将不断缩小,并且依旧不能用三种颜色染色。最终将会缩小成一个自环。一个自环将把整个环传染成一个完全图。

    如果一个图存在这样的染色方法,并且三种颜色都用上了,那么这个图必然还是只能红色点黄色点连边,黄色点只能向蓝色点连边,蓝色点只能向红色点连边。只要这个图存在一个三元环 (u ightarrow v,v ightarrow w,w ightarrow u) ,那么对于任意 (x)(u) 有边,那么必然会产生 (x ightarrow u,u ightarrow w,w ightarrow x) 。所以这样下去对于任意红色点会与任意黄色点右边,任意黄色点会向任意蓝色点有边,任意蓝色点会向绿色点有边。这样会产生 (rcdot y+ycdot b+bcdot r) 。其中 (r,y,b) 表示红色、黄色、蓝色点数量。

    如果存在染色方法并且至多两种颜色用上了,可以发现不能连出任何新边。

    这样做就行了。

    实现

    #include <cstdio>
    using namespace std;
    const int NN = 1e5 + 5, NM = 1e5 + 5;
    int N, M;
    struct E {
    	int v, nxt, fx;
    	E(int _v=0, int _nxt=0, int _fx=0) : v(_v), nxt(_nxt), fx(_fx) {}
    };
    struct Graph {
    	int head[NN], totE;
    	E edge[NM<<1];
    	void AddE(int u, int v, int fx) {
    		edge[++totE] = E(v, head[u], fx);
    		head[u] = totE;
    	}
    } G;
    int col[NN];
    bool vis[NN];
    int ys[] = {1, 2, 0};
    int fanys[] = {2, 0, 1};
    //0->1
    //1->2
    //2->0
    int cnt[3], egnum = 0, cc;
    bool Dfs(int u) {
    	vis[u] = 1;
    	cnt[col[u]] += 1;
    	int hoho = 0;
    	bool ret = 1;
    	for (int p = G.head[u]; p; p = G.edge[p].nxt) {
    		int v = G.edge[p].v;
    		++hoho;
    		if (vis[v]) {
    			if (G.edge[p].fx == 1 && col[v] != ys[col[u]]) {
    				ret = 0;
    			}
    			if (G.edge[p].fx == -1 && col[v] != fanys[col[u]]) {
    				//注意第一个 G.edge[p].fx == -1的条件
    				//以后限制能多写就多写 
    				ret = 0;
    			}
    			continue ;
    		}
    		if (G.edge[p].fx == 1)
    			col[v] = ys[col[u]];
    		else col[v] = fanys[col[u]];
    		if (!Dfs(v)) //记得这句话 
    			ret = 0;
    	}
    	egnum += hoho;
    	return ret;
    }
    int main() {
    	scanf("%d%d", &N, &M);
    	for (int i = 1; i <= M; ++i) {
    		int u, v;
    		scanf("%d%d", &u, &v);
    		G.AddE(u, v, 1);
    		G.AddE(v, u, -1);
    	}
    	long long ans = 0;
    	for (int i = 1; i <= N; ++i) {
    		if (!vis[i]) {
    			cnt[0] = cnt[1] = cnt[2] = 0;
    			egnum = 0; //初始化! 
    			bool jd = Dfs(i);
    			int sum = cnt[0] + cnt[1] + cnt[2];
    			//这里得 dfs完 cnt[0] cnt[1] cnt[2]才准确 
    			if (!jd) ans += (long long)sum * sum;
    			else if (!cnt[0] || !cnt[1] || !cnt[2]) ans += egnum / 2;
    			else ans += (long long)cnt[0] * cnt[1] +
    						(long long)cnt[1] * cnt[2] +
    						(long long)cnt[2] * cnt[0];
    		}
    	}
    	printf("%lld
    ", ans);
    	return 0;
    }
    /*
    5 4
    1 2
    2 3
    4 5
    5 5
    */
    

    总结

    • 和三元环有关系的题考虑用三种颜色染色?
  • 相关阅读:
    get_folder_size.ps1
    python3-database-shelve
    Windows中实现不依赖账户登录的开机启动程序
    SpringBoot+SpringDataJPA如何实现自定义且自由度高的查询[多表,多查询条件,多排序条件,分页,自定义sql封装]
    Windows phone 8.1之数据绑定(Data Binding)
    TextBox使用技巧--转载
    在Eclipse中使用git把项目导入到git中--转载
    运用多种知识点实现一个综合小游戏
    Git帮助之初始化项目设置向导
    如何从Eclipse导入github上的项目源码--转载
  • 原文地址:https://www.cnblogs.com/YouthRhythms/p/13795035.html
Copyright © 2020-2023  润新知