BZOJ 1453 [WC] 双面棋盘 并查集+线段树暴搞
2016-02-25 16:37
120 查看
这道题大部分这么做的题解写的都很简洁,不过看代码就能看懂大概搞法。
线段树的一个节点维护的信息有:节点对应区间[l,r]为原图第l行到第r行这(r-l+1)×n的棋盘中的黑色连通块数和白色连通块数。然后我们保存第l行和第r行的连通的情况,这样就可以方便地合并两个相邻区间了。注意到同一行中不相邻的两个块可能是属于一个连通块的,所以用并查集来储存连通的信息比较合适。每次合并都是O(n)。合并区间时,连通块数容易维护,而并查集就比较麻烦,细节较多,仔细想想写对就好。
线段树的一个节点维护的信息有:节点对应区间[l,r]为原图第l行到第r行这(r-l+1)×n的棋盘中的黑色连通块数和白色连通块数。然后我们保存第l行和第r行的连通的情况,这样就可以方便地合并两个相邻区间了。注意到同一行中不相邻的两个块可能是属于一个连通块的,所以用并查集来储存连通的信息比较合适。每次合并都是O(n)。合并区间时,连通块数容易维护,而并查集就比较麻烦,细节较多,仔细想想写对就好。
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; struct node { int b, w, f[808]; void init (int n) { b = w = 0; for (int i = 0; i <= n; i++) f[i] = i; } int find (int x) { if (f[x] == x) return x; return f[x] = find(f[x]); } void merge (int a, int b) { f[find(a)] = find(b); } }; int n, m; bool map[205][205]; struct SegmentTree { node T[808]; #define LChild p<<1,l,mid #define RChild p<<1|1,mid+1,r void maintain (int p, int l, int r) { int lc = p<<1, rc = lc|1, mid = (l+r)/2; T[p].init(n<<2); T[p].b = T[lc].b + T[rc].b; T[p].w = T[lc].w + T[rc].w; for (int i = 1; i <= n; i++) T[p].f[i] = T[lc].f[i], T[p].f[i+n] = T[lc].f[i+n], T[p].f[i+n+n] = T[rc].f[i]+n*2, T[p].f[i+n+n+n] = T[rc].f[i+n]+n*2; for (int i = 1; i <= n; i++) if (map[mid][i]==map[mid+1][i]) { int f1 = T[p].find(i+n); int f2 = T[p].find(i+n+n); if (f1 != f2) { T[p].w -= (map[mid][i]==0), T[p].b -= (map[mid][i]==1); T[p].merge(f1,f2); } } for (int i = 1; i <= n; i++) { int fi = T[p].find(i); if (fi > n) T[p].f[fi] = T[p].f[i] = i; } for (int i = n*3+1; i <= n*4; i++) { int fi = T[p].find(i); if (fi <= n) T[p].f[i-n*2] = T[p].f[i]; else T[p].f[fi] = T[p].f[i-n*2] = i-n*2; } } void make (int r, int p) { T[p].init(n<<2); for (int i = 1; i <= n; i++) T[p].merge(i,i+n); for (int i = 2; i <= n; i++) if (map[r][i]==map[r][i-1]) { T[p].merge(i,i-1); T[p].merge(i+n,i+n-1); } for (int i = 1; i <= n; i++) if (T[p].f[i] == i) T[p].w += map[r][i]==0, T[p].b += map[r][i]==1; for (int i = 1; i <= n; i++) if (T[p].f[i+n] == i+n) T[p].w += map[r][i]==0, T[p].b += map[r][i]==1; } void build (int p, int l, int r) { if (l == r) make(r,p); else { int mid = (l+r)/2; build(LChild); build(RChild); maintain(p,l,r); } } void change (int p, int l, int r, int pos) { if (l == r) {make(r,p);return;} int mid = (l+r)/2; if (pos <= mid) change(LChild,pos); else change(RChild,pos); maintain(p,l,r); } void change (int x, int y) { map[x][y] = !map[x][y]; change (1, 1, n, x); } void query () { printf ("%d %d\n", T[1].b, T[1].w); } }Solve; int main () { scanf ("%d", &n); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) scanf ("%d", map[i]+j); Solve.build(1,1,n); scanf ("%d", &m); for (int i = 1; i <= m; i++) { int x, y; scanf ("%d %d", &x, &y); Solve.change(x,y); Solve.query(); } return 0; }
相关文章推荐
- github入门实践(本地端)
- 我的思想轨迹
- Android studio出现Error:Plugin is too old, please update to a more recent version, or set ANDROID_DAIL
- 详解 iOS navigationBar 的设置问题
- Java GC系列(1):Java垃圾回收简介
- doctrine源码之dbal目录
- ActiveMQ 即时通讯服务 浅析
- 程序出现警告,解决方式
- 40个UI设计工具
- linux设备驱动归纳总结(五):3.操作硬件——IO静态映射
- 不停的往android手机串口写数据
- wordpress 使用贴图库插件,经行图片外链!
- Unity C# 脚本教程
- Burp模糊测试---简单扩展工具
- Android 分渠道打包原理
- Linux中安装配置ftp服务器方法
- Serializer序列化class,生成Xml文件详解
- Android R.java类生成工具
- VMware10下安装CentOS 6.5+基本网络配置
- 移动端rem单位用法[转]