博客
关于我
[CF1444C]Team-Building
阅读量:307 次
发布时间:2019-03-04

本文共 2715 字,大约阅读时间需要 9 分钟。

题目

思路

思路一定要开阔……我一开始 y y \tt yy yy 了个什么做法哟……

我一开始直接猜是 d f s \tt dfs dfs 树这种线性的东西。然后开始努力的优化。首先思考到 “异色边” 只需要走过一次,因为一旦走过这个 “异色边” ,只要你认真地深搜,就会搜出这个环。但是 “同色边” 就不行了,所以考虑同色连通块缩点,缩成两个点即可(因为我们只在乎环的奇偶)。

然后一个点会被访问很多次。考虑把边排序后 l o w e r _ b o u n d \tt lower\_bound lower_bound ,发现仍然不行,复杂度假了。于是考虑直接把所有同类的异色边拿出来。此时才发现是 并查集

意识到带权并查集可以处理二分图之后,一切问题都解决了,只需要一个可回退的并查集就行。但是我在这里吃了个教训:回退不能改变树结构,必须是按秩合并。一旦路径压缩你就完蛋了。 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)

当然,如果你把连通块缩点了,你也可以路径压缩,把所有波及到的连通块暴力连接回去。可以优化到 O ( α n ) \mathcal O(\alpha n) O(αn) ,但是我们并不需要这么优秀 😂


2020 / 12 / 4    u p d a t e \tt 2020/12/4\;update 2020/12/4update

突然意识到原本的做法完!全!可!行!(指同色连通块缩成两个点)

为啥一定要在原图上跑 d f s \tt dfs dfs 树?我们姑且把边放在 v e c t o r \tt vector vector 里,每次用链式前向星加入有用的边然后造 d f s \tt dfs dfs 树不就行了?这不就是线性的了?这不就是 O ( n + m ) \mathcal O(n+m) O(n+m) 随便跑?

缩点也并不麻烦,每个连通块内随便找个点,然后到它距离为奇数和偶数的分到两边。

我感觉我真是蠢到爆炸……全地球可能很难找出一个像我一样蠢的蠢货了!

代码

#include 
#include
#include
#include
using namespace std;typedef long long int_;inline int readint(){ int a = 0; char c = getchar(), f = 1; for(; c<'0'||c>'9'; c=getchar()) if(c == '-') f = -f; for(; '0'<=c&&c<='9'; c=getchar()) a = (a<<3)+(a<<1)+(c^48); return a*f;}const int MaxN = 500005;struct UFS{ vector< int > zxy; // fucked int fa[MaxN], d[MaxN]; int rnk[MaxN]; // 启发式合并 inline int find(int a,int &x){ if(a == fa[a]) return a; return find(fa[a],x ^= d[a]); } void init(int n){ for(int i=1; i<=n; ++i){ fa[i] = i, d[i] = 0; rnk[i] = 1; } zxy.clear(); } /** @return 是否仍然为二分图 */ inline bool merge(int a,int b){ int da = 0, x = find(a,da); int db = 0, y = find(b,db); if(x == y) return da != db; if(rnk[x] > rnk[y]) swap(x,y); fa[x] = y, d[x] = da^db^1; rnk[y] += rnk[x]; // ensure Complexity zxy.push_back(x); return true; } void restore(){ while(!zxy.empty()){ int x = zxy.back(); rnk[fa[x]] -= rnk[x]; fa[x] = x, d[x] = 0; zxy.pop_back(); } }};UFS xyx; // 并查集vector< pair
> cu; // 不铜的边int col[MaxN]; // 每个点的颜色bool cmp(const pair
&a,const pair
&b){ if(col[a.first] != col[b.first]) return col[a.first] < col[b.first]; return col[a.second] < col[b.second];}bool junk[MaxN]; // 不行的颜色int main(){ int n = readint(), m = readint(); int k = readint(); for(int i=1; i<=n; ++i) col[i] = readint(); xyx.init(n); // 同色连通块直接连出来 for(int i=1,a,b; i<=m; ++i){ a = readint(), b = readint(); if(col[a] == col[b]) junk[col[a]] = junk[col[a]] or !xyx.merge(a,b); else{ if(col[a] > col[b]) swap(a,b); // 使其无序 cu.push_back(make_pair(a,b)); } } xyx.zxy.clear(); // 把 zxy 清空 sort(cu.begin(),cu.end(),cmp); int_ ans = 0; // 最终答案 for(int i=1,now=0; i<=k; ++i) if(!junk[i]) // 垃圾颜色跳过 ans += now, ++ now; for(int l=0,r=0,len=cu.size(); l

转载地址:http://oitq.baihongyu.com/

你可能感兴趣的文章
java学习笔记31:Arrays类介绍使用
查看>>
java学习笔记36:Integer的基本方法
查看>>
java学习笔记37:Long的基本方法
查看>>
java并发学习2:线程的应用
查看>>
java并发学习12:问题引入
查看>>
java并发学习20:park与unpark
查看>>
java并发学习24:固定运行顺序模式
查看>>
html5学习9:HTML5文档结构详解
查看>>
介绍一个不错的分析客户价值的模型RFM
查看>>
SpringMVC---使用
查看>>
2.2.4 加减法运算和溢出判断更换
查看>>
2.2.6 强制类型转换
查看>>
计算机网络教程 谢希仁 第三章 数据链路层
查看>>
Redis缓存数据的处理流程
查看>>
Linux:文件句柄泄漏问题
查看>>
Linux:多线程简介
查看>>
ACM-ICPC寒假算法训练1:搜索 HDOJ P1010 : Tempter of the Bone 奇偶剪枝分析
查看>>
【java】316. 去除重复字母----学会栈的使用
查看>>
【java】227. 基本计算器 II---思路简单,代码清晰!!!
查看>>
【java】115. 不同的子序列----学会动态规划,时间复杂度O(n^2)!!!
查看>>