专题突破三之并查集Ⅱ——星球大战,In Touch,方格染色,Junk-Mail Filter,关押罪犯,Silver Woods,Must Be Rectangular!
文章目录
- [JSOI2008]星球大战
- In Touch
- 方格染色
- Junk-Mail Filter
- [NOIP2010 提高组] 关押罪犯
- Silver Woods
- Must Be Rectangular!
[JSOI2008]星球大战
source
非常套路的,正着打击星球,逆着就是添加星球以及关系,并查集维护此时连通块个数
就是这个星球被打击前的答案
#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;
#define maxn 400005
vector < int > h[maxn];
pair < int, int > p[maxn];
int n, m, k, ans;
int f[maxn], g[maxn], ret[maxn];
bool used[maxn], vis[maxn];void makeset() {for( int i = 0;i < n;i ++ ) f[i] = i;
}int find( int x ) {return x == f[x] ? x : f[x] = find( f[x] );
}void merge( int u, int v ) {u = find( u ), v = find( v );if( u ^ v ) ans --, f[v] = u;
}int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= m;i ++ ) {scanf( "%d %d", &p[i].first, &p[i].second );h[p[i].first].push_back( i );h[p[i].second].push_back( i );}scanf( "%d", &k );makeset();for( int i = 1;i <= k;i ++ )scanf( "%d", &g[i] ), used[g[i]] = 1;ans = n - k;for( int i = 1;i <= m;i ++ )if( ! used[p[i].first] and ! used[p[i].second] )merge( p[i].first, p[i].second );for( int i = k;i;i -- ) {ret[i] = ans;ans ++;used[g[i]] = 0;for( auto t : h[g[i]] )if( vis[t] ) continue;else if( used[p[t].first] or used[p[t].second] )continue;else merge( p[t].first, p[t].second );}printf( "%d\n", ans );for( int i = 1;i <= k;i ++ )printf( "%d\n", ret[i] );return 0;
}
In Touch
source
转化成最短路问题,套用dijkstra\rm dijkstradijkstra,每个点都只访问一次,但是范围那么大,用并查集帮助跳过已访问点,直指新点
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
#define Pair pair < int, int >
#define int long long
#define maxn 200005
#define inf 1e15
priority_queue < Pair, vector < Pair >, greater < Pair > > q;
int T, n;
int dis[maxn], f[maxn], L[maxn], R[maxn], c[maxn];
int MS[2] = { -1, 1 };int find( int x ) {return x == f[x] ? x : f[x] = find( f[x] );
}signed main() {scanf( "%lld", &T );while( T -- ) {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &L[i] );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &R[i] );for( int i = 1;i <= n;i ++ ) scanf( "%lld", &c[i] );for( int i = 0;i <= n + 1;i ++ ) f[i] = i, dis[i] = inf;q.push( make_pair( dis[1] = c[1], 1 ) );while( ! q.empty() ) {int now = q.top().second; q.pop();for( int k = 0;k < 2;k ++ ) {int l = now + L[now] * MS[k];int r = now + R[now] * MS[k];if( l > r ) swap( l, r );l = min( l, n + 1 );l = max( l, 1ll );if( l > r ) continue;for( int nxt = l;;nxt ++ ) {nxt = find( nxt );if( nxt <= 0 || nxt > n || nxt > r ) break;if( dis[nxt] > dis[now] + c[nxt] ) {dis[nxt] = dis[now] + c[nxt];q.push( make_pair( dis[nxt], nxt ) );}f[find( nxt )] = find( nxt + 1 );}}}printf( "0" );for( int i = 2;i <= n;i ++ )if( dis[i] == inf ) printf( " -1" );else printf( " %lld", dis[i] - c[i] );printf( "\n" );}return 0;
}
方格染色
source
observation1:
只要确定了第一行和第一列,整张表格就被确定了
observation2:
g1,1⨁gi,1⨁g1,j⨁gi,j=[imod2=0andjmod2=0]g_{1,1}\bigoplus g_{i,1}\bigoplus g_{1,j}\bigoplus g_{i,j}=[i\ \rm mod\ 2=0\ and\ j\ mod\ 2=0]g1,1⨁gi,1⨁g1,j⨁gi,j=[i mod 2=0 and j mod 2=0]
可以通过枚举g1,1g_{1,1}g1,1的状态,通过已知的gi,jg_{i,j}gi,j来判断gi,1,g1,jg_{i,1},g_{1,j}gi,1,g1,j的关系
这样形成了若干组有且仅有两个选项的约束关系,带权并查集维护
连通块内有一个取值定了,其他的取值也定了
如果有解,答案就是2^(连通块数量-1)
,减1是g1,1g_{1,1}g1,1的状态因为是枚举的,算已知的
#include <cstdio>
#define int long long
#define mod 1000000000
#define maxn 2000005
int n, m, k;
bool flag0, flag1;
int x[maxn], y[maxn], c[maxn], f[maxn], w[maxn];int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans;
}int find( int x ) {if( x == f[x] ) return x;else {int fa = f[x];f[x] = find( f[x] );w[x] = ( w[x] + w[fa] ) % 2;return f[x];}
}int solve() {for( int i = 1;i <= n + m;i ++ ) f[i] = i, w[i] = 0;f[n + 1] = 1;for( int i = 1;i <= k;i ++ ) {if( x[i] == 1 && y[i] == 1 ) continue;int u = find( x[i] ), v = find( y[i] + n );int t = w[x[i]] ^ w[y[i] + n] ^ c[i] ^ ( x[i] & 1 or y[i] & 1 ) ^ 1;if( u == v and t ) return 0;f[v] = u, w[v] = t;} int cnt = 0;for( int i = 1;i <= n + m;i ++ )if( find( i ) == i ) cnt ++;return qkpow( 2, cnt - 1 );
}signed main() {scanf( "%lld %lld %lld", &n, &m, &k );for( int i = 1;i <= k;i ++ ) {scanf( "%lld %lld %lld", &x[i], &y[i], &c[i] );if( x[i] == 1 && y[i] == 1 )if( ! c[i] ) flag0 = 1;else flag1 = 1;else;}int ans0 = solve();//c[g[1][1]]=0 bluefor( int i = 1;i <= k;i ++ ) c[i] ^= 1;//c[g[1][1]]=1 redint ans1 = solve();if( flag0 ) ans1 = 0;if( flag1 ) ans0 = 0;printf( "%lld\n", ( ans0 + ans1 ) % mod );return 0;
}
Junk-Mail Filter
source
可删除并查集模板
只需要注意一下被删除前的集合若只有一个,元素被删除后集合也就不存在了
#include <cstdio>
#include <iostream>
using namespace std;
#define maxn 2200000
int n, m, ans, cnt;
int f[maxn], siz[maxn];void makeset() {cnt = n;for( int i = 0;i < n;i ++ )f[i] = f[i + n] = cnt ++, siz[i + n] = 1;
}int find( int x ) {return x == f[x] ? x : f[x] = find( f[x] );
}void merge( int u, int v ) {u = find( u ), v = find( v );if( siz[u] < siz[v] ) swap( u, v );if( u ^ v )siz[u] += siz[v], f[v] = u, ans --;
}void Delete( int x ) {int fx = find( x );siz[fx] --;if( ! siz[fx] ) ans --;f[x] = f[cnt] = cnt;siz[cnt] = 1;cnt ++;ans ++;
}int main() {int T = 0;while( scanf( "%d %d", &n, &m ) ) {if( ! n and ! m ) return 0;makeset();ans = n;char opt[5]; int x, y;for( int i = 1;i <= m;i ++ ) {scanf( "%s", opt );if( opt[0] == 'M' ) {scanf( "%d %d", &x, &y );merge( x, y );}else {scanf( "%d", &x );Delete( x );}}printf( "Case #%d: %d\n", ++ T, ans );}return 0;
}
[NOIP2010 提高组] 关押罪犯
source
贪心,先把冲突最大的安排,可以选择拆点i,i+ni,i+ni,i+n,两个人不同合并(i,j+n)/(i+n,j)(i,j+n)/(i+n,j)(i,j+n)/(i+n,j)
也可以选择带权并查集在%2\% 2%2意义下做
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100005
struct node {int u, v, w;node(){}node( int U, int V, int W ) {u = U, v = V, w = W;}
}r[maxn];
int n, m;
int f[maxn];void makeset() {for( int i = 1;i <= ( n << 1 );i ++ ) f[i] = i;
}int find( int x ) {return x == f[x] ? x : f[x] = find( f[x] );
}void merge( int u, int v ) {u = find( u ), v = find( v ), f[v] = u;
}int main() {scanf( "%d %d", &n, &m );for( int i = 1, u, v, w;i <= m;i ++ ) {scanf( "%d %d %d", &u, &v, &w );r[i] = node( u, v, w );}makeset();sort( r + 1, r + m + 1, []( node x, node y ) { return x.w > y.w; } );for( int i = 1;i <= m;i ++ ) {int u = r[i].u, v = r[i].v;if( find( u ) == find( v ) )return ! printf( "%d\n", r[i].w );elsemerge( u, v + n ), merge( v, u + n );}printf( "0\n" );return 0;
}
Silver Woods
source
跟一道奶酪题相似,奶酪是空心球,老鼠从底到顶;这道题是从左到右
二分半径,然后将圆卡不过的连接起来
具体而言就是点点之间距离小于直径,点和上下界面距离小于直径,合并起来
并查集判断上界面和下界面是否相通,不通证明有一种方法圆可以通过
#include <cstdio>
#include <cmath>
#define eps 1e-6
#define maxn 105
int n;
double x[maxn], y[maxn];
int f[maxn];void makeset() {for( int i = 0;i <= n + 1;i ++ ) f[i] = i;
}int find( int x ) {return x == f[x] ? x : f[x] = find( f[x] );
}void merge( int u, int v ) {u = find( u ), v = find( v ), f[v] = u;
}double dis( int i, int j ) {return sqrt( ( x[i] - x[j] ) * ( x[i] - x[j] ) + ( y[i] - y[j] ) * ( y[i] - y[j] ) );
}bool check( double r ) {double d = r * 2;makeset();for( int i = 1;i <= n;i ++ ) {if( y[i] + d >= 100 )merge( 0, i );if( y[i] - d <= -100 )merge( n + 1, i );for( int j = i + 1;j <= n;j ++ )if( dis( i, j ) < d )merge( i, j );}if( find( 0 ) == find( n + 1 ) ) return 0;else return 1;
}int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ )scanf( "%lf %lf", &x[i], &y[i] );double l = 0, r = 100, ans;while( r - l > eps ) {double mid = ( l + r ) / 2;if( check( mid ) ) ans = mid, l = mid;else r = mid;}printf( "%.6f\n", ans );return 0;
}
Must Be Rectangular!
source
对于每个坐标(xi,yi)(xi,yi)(xi,yi),xixixi向yi+nyi+nyi+n连边,形成一个二分图
然后后续添加点实际上就是对这个二分图的每个分量补成完全二分图
并查集维护一下二分图的边的个数,两个部的点的个数
#include <cstdio>
#define maxn 100000
int n;
int f[maxn << 2], row[maxn << 2], col[maxn << 2];void makeset() {for( int i = 1;i <= ( maxn << 1 );i ++ )f[i] = i;
}int find( int x ) {return x == f[x] ? x : f[x] = find( f[x] );
}void merge( int u, int v ) {u = find( u ), v = find( v ), f[v] = u;
}int main() {scanf( "%d", &n );makeset();for( int i = 1, x, y;i <= n;i ++ ) {scanf( "%d %d", &x, &y );merge( x, y + maxn );}long long ans = 0;for( int i = 1;i <= maxn;i ++ ) row[find( i )] ++;for( int i = 1;i <= maxn;i ++ ) col[find( i + maxn )] ++;for( int i = 1;i <= maxn;i ++ ) ans += 1ll * row[i] * col[i];printf( "%lld\n", ans - n );return 0;
}
{
u = find( u ), v = find( v ), f[v] = u;
}
int main() {
scanf( “%d”, &n );
makeset();
for( int i = 1, x, y;i <= n;i ++ ) {
scanf( “%d %d”, &x, &y );
merge( x, y + maxn );
}
long long ans = 0;
for( int i = 1;i <= maxn;i ++ ) row[find( i )] ++;
for( int i = 1;i <= maxn;i ++ ) col[find( i + maxn )] ++;
for( int i = 1;i <= maxn;i ++ ) ans += 1ll * row[i] * col[i];
printf( “%lld\n”, ans - n );
return 0;
}
专题突破三之并查集Ⅱ——星球大战,In Touch,方格染色,Junk-Mail Filter,关押罪犯,Silver Woods,Must Be Rectangular!相关推荐
- HDU-3974 Assign the task 线段树 或 直接模拟多叉树 或 并查集 (三种方法)
题目大意 t 组数据(t<=10),每组第一行一个 n 表示 n 个员工(n<=5e4),接下来 n-1 行,每行两个整数 u,v 表示 v 是 u 的上司 然后一行 m 表示有 m 个操 ...
- 数据结构 之 并查集(Disjoint Set)
一.并查集的概念: 首先,为了引出并查集,先介绍几个概念: 1.等价关系(Equivalent Relation) 自反性.对称性.传递性. 假设a和b存在等价关系.记 ...
- 简单易懂的并查集算法以及并查集实战演练
文章目录 前言 一.引例 二.结合引例写出并查集 1. 并查集维护一个数组 2. 并查集的 并 操作 3. 并查集的 查 操作 4. 基本并查集模板代码实现--第一版(有错误后面分析) 4.1 Jav ...
- 下班前几分钟,我彻底弄懂了并查集
目录 一.并查集的由来 二.代表元法 2.1 初始化 2.2 查询 2.3 合并 2.4 设计理念 三.并查集的应用 3.1 合并集合 3.2 连通块中点的数量 3.3 亲戚 3.4 省份数量 Ref ...
- 【常用数据结构——并查集(又在乱牵线了)】
并查集 简介 并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中.这一类问题 ...
- java并查集_一个非常实用而且精妙的算法-并查集(java语言实现)
在学习数据结构的时候,老师多少会提到并查集,他的应用也是超级广泛.本文首先会通过案例来对并查集有一个介绍.然后给出并查集的java实现. 一.并查集原理 话说在江湖上有很多门派,这些门派相互争夺武林霸 ...
- 并查集是什么?怎么模拟实现?如何应用?
目录 一.什么是并查集? 二.并查集可以解决哪些问题? 三.并查集的模拟实现 3.1.并查集的定义 3.2.查询两个元素是否是同一个集合 3.3.合并两个集合 3.4.求集合个数 3.5.并查集完整代 ...
- 通俗易懂超有爱的并查集~~~
一,杭电oj并查集题目-畅通工程 二,简单分析题目大意 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的.最后要解决的是整幅图的连通性问题.比如随意给你两个 ...
- c++入门必学算法 并查集
一.什么是并查集 并查集其实就是实现一个类似朋友圈的功能,朋友的朋友是朋友,朋友的朋友的朋友也是朋友,即只要有关系一些人就合并成为一个朋友圈. 并查集可以实现查询两个人是否是朋友,查询朋友圈的个数 二 ...
最新文章
- 电脑开机进入不了XP界面
- EasyUI 在aspx页面显示高度不正常解决办法
- ubuntu下安装拼音输入法ibus
- mysql语法题_mysql数据库题语法练习
- 2018年最值得关注的15大技术趋势
- Cas单点登录配置数据查询用户
- Python中的Monkey Patch(猴子补丁)
- Python并发编程Futures
- Linux--vmlinuz、vmlinux、initrd
- <blockquote>标签 自定义样式
- aloha协议c语言实现,任务ALOHA协议的OPNET仿真.doc
- 你的第一桶金是如何赚到的?
- 2021-下载酷狗音乐-爬虫-java
- XML报表打印出错,乱码问题
- 前端展示json格式数组
- linux基础篇,数据流重定向
- 《 计算机应用基础》模拟试卷,《计算机应用基础》模拟试卷一(附答案).doc
- 使用Testin云测试进行兼容测试
- Java经典面试:完美世界java开发待遇
- python 和linux环境下:音频处理变频变调的方法和框架
热门文章
- Python难懂?买一次西瓜就懂了!
- 卸载wrapt_[python] 安装TensorFlow问题 解决Cannot uninstall 'wrapt'. It is a distutils installed project...
- 不同用户同时并发测压_简单聊聊吞吐量(TPS)、QPS、并发数、响应时间(RT)概念...
- php获取qzonetoken,QQ一键登录实现
- 华为二面!!!被问常用API,这也太偏门了吧,我秀了一波hhhh~
- 初二物理模型有哪些_初二是成绩下滑的高危期,做好这5点成绩涨涨涨!(附全学科提升技巧,家长转给孩子!)...
- php html asp .net iis tomcat,iis+apache+tomcat 整合共享80口 支持ASP .NET JSP PHP全能WEB服务...
- csv 字符串_python3从零学习-5.5.1、CSV 文件读写
- 一文弄懂Numpy中ndarray的维度(dimension)/轴数(axis/axes)问题
- [XML-Jsoup]Jsoup_对象的使用(Jsoup工具类,Document,Elements,Element,Node)