2017暑期集训 Day 3 搜索与并查集
来源:互联网 发布:剑灵24人本优化不卡 编辑:程序博客网 时间:2024/05/15 15:21
A - 食物链
[solution]
并查集新操作!
维护决策的矛盾信息,每次决策之间的选择是互相矛盾的
每个动物可能为三种情况:A、B、C,i代表第i种动物是A类动物,i+n代表第i种动物是B类,i+2*n是c类,这样
对于d=1时,x、y为同类动物,即如果x是a类,则y也为a类,即unite(x,y),同理,都为b类,unite(x+n,y +n),同理,unite(x+2*n,y+2*n)
对于d=2时,x吃y,如果x是a类,则y是b类,即unite(x, y + n),同理unite(x + n, y + 2*n), unite(x +2*n, y),下面考虑矛盾的情况,对于d=1时,x、y会有九种状态,出去最后操作的三种状态,另外六种状态都是不允许出现的,即(x, y), (x + n, y + n), (x + 2n, y +2n), (x, y + 2n),(x + n, y), (x + 2n, y + n)难道我们必须打6个if吗?我们观察这六种状态,前三种是等价的,真假值相同,这样我们每次判断两个即可
d=2类似
[Code]
#include<cstdio>#include<iostream>#include<set>using namespace std;const int N = 200000 + 500;int f[N];int n, m;int find(int x){ if (f[x] == x) return x; return f[x] = find(f[x]);}void unio(int x, int y){ int xx = find(x); int yy = find(y); if (xx != yy) f[xx] = yy;}bool cal(int x, int y){ int xx = find(x); int yy = find(y); return xx == yy;}int main(){ // freopen("a.in", "r", stdin); scanf("%d%d", &n, &m); for(int i = 1; i <= n * 3; i++) f[i] = i; int ans = 0; for(int i = 1; i <= m; i++) { int d, x, y; scanf("%d%d%d", &d, &x, &y); if (x < 1 || x > n || y < 1 || y > n) { ans++; continue; } if (d == 1) { if (cal(x, y + n) || cal(x, y + 2 * n)) { ans++; continue; } unio(x, y); unio(x + n, y + n); unio(x + 2 * n, y + 2 * n); } else { if (x == y) { ans++; continue; } if (cal(x, y) || cal(x, y + 2 * n)) { ans++; continue; } unio(x, y + n); unio(x + n, y + 2 * n); unio(x + 2 * n, y); } } printf("%d", ans); return 0;}
B - Wireless Network
[Solution]
一个朴素的二分
[Code]
#include<cstdio>#include<iostream>using namespace std;const int N= 200000;int f[N];int n, m;bool rep[N];double x[N], y[N];int find(int x){ if (f[x] == x) return x; return f[x] = find(f[x]);}int union1(int x, int y){ // printf("%d %d\n", x, y); int xx = find(x), yy = find(y); if (xx != yy) f[xx] = yy;}int main(){ // freopen("a.in", "r", stdin); int n; double d; scanf("%d", &n); scanf("%lf", &d); for(int i = 1; i <= n; i++) scanf("%lf%lf", &x[i], &y[i]); for(int i = 1; i <= n; i++) f[i] = i; string s; while(cin>>s) { if (s == "O") { int k; scanf("%d", &k); for(int i = 1; i <= n; i++) if (k != i) { rep[k] = true; double dis = (x[i] - x[k]) * (x[i] - x[k]) + (y[i] - y[k]) * (y[i] - y[k]); if (rep[i] && dis <= d * d) union1 (i, k); } } else { int i, j; scanf("%d%d", &i, &j); int ii = find(i); int jj = find(j); if (ii == jj) printf("SUCCESS\n"); else printf("FAIL\n"); } } return 0;}
C - The Door Problem
[Solution]
这个思路和A类似,对于每个门都对应两份switch,每个switch有两种状态,这样我们对于每个门进行分析,如果此门的状态是锁住的,我们这两个开关分别是一开一关,因此unite(x,y +n)
|unite(x+n,y), 反之则同时开启或同时关闭
[Code]
#include<cstdio>#include<iostream>using namespace std;const int N = 200000 + 500;int f[N];int a[N][5], n, m, tot[N];int find(int x){ if (f[x] == x) return x; return f[x] = find(f[x]);}void unio(int x, int y){ int xx = find(x); int yy = find(y); if (xx != yy) f[xx] = yy;}bool cal(int x, int y){ int xx = find(x); int yy = find(y); return xx == yy;}int main(){ // freopen("a.in", "r", stdin); scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &tot[i]); for(int i = 1; i <= 2 * m; i++) f[i] = i; for(int i = 1; i <= m; i++) { int top; scanf("%d", &top); for(int j = 1; j <= top; j++) { int y; scanf("%d", &y); if (a[y][0] != 0) a[y][1] = i; else a[y][0] = i; } } for(int i = 1; i <= n; i++) { int x = a[i][0], y = a[i][1]; if (tot[i] == 0) { if (cal(x, y) || cal(x + m, y + m)) { printf("NO"); return 0; } unio(x, y + m); unio(x + m, y); } else { if (cal(x, y + m) || cal(x + m, y)) { printf("NO"); return 0; } unio(x, y); unio(x + m, y + m); } } printf("YES"); return 0;}
D - 小希的迷宫
[Solution]
判断一副图是不是树,此题坑点很多。。。
首先很容易想到每次加边的时候判断两端点所处的集合是否相同,,,但是最后你还得确保此图联通,所以判断一下点的个数,与边的个数相比较一下即可
注意00的情况
[Code]
#include<cstdio>#include<iostream>#include<set>using namespace std;const int N = 200000 + 500;int f[N];int s[N], t[N], top;set<int> point;int find(int x){ if (f[x] == x) return x; return f[x] = find(f[x]);}void unio(int x, int y){ int xx = find(x); int yy = find(y); if (xx != yy) f[xx] = yy;}bool cal(int x, int y){ int xx = find(x); int yy = find(y); return xx == yy;}bool calc(){ for(int i = 1; i <= top; i++) { int x = s[i], y = t[i]; if (cal(x, y)) { // printf("This is %d %d \n", x, y); return false; } if (point.count(x) == 0) point.insert(x); if (point.count(y) == 0) point.insert(y); unio(x, y); } if (point.size() != top + 1) return false; return true;}int main(){ // freopen("a.in", "r", stdin); int x, y; top = 0; while(~scanf("%d%d", &x, &y)) { if (x == -1 && y == -1) break; if (x != 0) { top++; s[top] = x; t[top] = y; } else { if (top == 0) { printf("Yes\n"); continue; } for(int i = 1; i <= 100000; i++) f[i] = i; if (calc()) printf("Yes\n"); else printf("No\n"); top = 0; point.clear(); } } return 0;}
E - Island Puzzle
[Solution]
我们注意到每次只能把数字移动到0的位置,这样这些数字之间的相对顺序是不会改变的,这样我们读取的时候忽略掉0,把a串写两遍,判断b串是否是a串的一个子串即可
[Code]
#include<cstdio>#include<iostream>using namespace std;const int N= 400000 + 500;int a[N], b[N];int n;bool calc(int l, int r){ int x = l, y = 1; for(int i = 1; i <= n; i++) { if (a[x] != b[y]) return false; x++; y++; } return true;}int main(){ //freopen("a.in", "r", stdin); scanf("%d", &n); int top = 0; for(int i = 1; i <= n; i++) { int x; scanf("%d", &x); if (x != 0) a[++top] = x; } top = 0; for(int i = 1; i <= n; i++) { int x; scanf("%d", &x); if (x != 0) b[++top] = x; } n--; for(int i = 1; i <= n; i++) a[i + n] = a[i]; for(int i = 1; i <= n; i++) if (a[i] == b[1]) { if (calc(i, i + n - 1)) printf("YES"); else printf("NO"); return 0; } return 0;}
F - The Tag Game
[Solution]
很有意思的一道题目,显然,最后相遇到叶子节点,我们跑一边树形dp,预处理每个结点到以此节点为根的子树的最大路径f[i],这样我们跑一遍第一个人能到达的结点i,ans=max(f[i] + dis[i]),disp[i]为根到i的距离
[Code]
#include<cstdio>#include<iostream>#include<queue>#include<vector>using namespace std;const int N= 400000 + 500;int dis[N], dp[N], par[N];bool f[N];vector<int> a[N];vector<int> tree[N];int n, m;void build(int x){ for(int i = 0; i < a[x].size(); i++) { int y = a[x][i]; if (f[y]) continue; tree[x].push_back(y); par[y] = x; f[y] = true; build(y); }}void treedp(int x){ if (a[x].size() == 0) { dp[x] = 0; return ; } for(int i = 0; i < tree[x].size(); i++) { int y = tree[x][i]; treedp(y); dp[x] = max(dp[x], dp[y] + 1); }}int main(){ // freopen("a.in", "r", stdin); scanf("%d%d", &n, &m); for(int i = 1; i < n; i++) { int x, y; scanf("%d%d", &x, &y); a[x].push_back(y); a[y].push_back(x); } f[1] = true; build(1); treedp(1); int x = m; int ans = 0; while(x != 1) { ans++; x = par[x]; } int tot = -1, answer = 0; x = m; while(x != 1) { tot++; if (tot >= ans - tot) break; answer = max(answer, ans - tot + dp[x]); //* printf("%d\n", x); x = par[x]; } printf("%d", answer * 2); return 0;}
G - Mike and Shortcuts
[Solution]
很裸的bfs
[Code]
#include<cstdio>#include<iostream>#include<queue>using namespace std;const int N= 400000 + 500;int a[N], dis[N], tle[100];int n, s, t;bool f[N];queue<int >q;int main(){ //freopen("a.in", "r", stdin); scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= n; i++) dis[i] = n + 250; dis[1] = 0; q.push(1); f[1] = true; while(!q.empty()) { int x = q.front(); q.pop(); tle[1] = x - 1; tle[2] = x + 1; tle[3] = a[x]; for(int i = 1; i <= 3; i++) { int y = tle[i]; if (y < 1 || y > n) continue; if (!f[y] && dis[y] > dis[x] + 1) { dis[y] = dis[x] + 1; // printf("%d -- > %d %d\n", x, y, dis[y]); f[y] = true; q.push(y); } } } printf("0"); for(int i = 2; i <= n; i++) printf(" %d", dis[i]); return 0;}
H - A strange lift
[Solution] dp 搞一下,用时间作为顺序即可
[Code]
#include<cstdio>#include<iostream>using namespace std;const int N= 400000 + 500;int f[N], a[N];int n, s, t;int main(){ //freopen("a.in", "r", stdin); while(~scanf("%d", &n) && n) { scanf("%d%d", &s, &t); for(int i = 1; i <= n ; i++) scanf("%d", &a[i]); for(int i = 1; i <= n; i++) f[i] = -1; f[s] = 0; for(int i = 1; i <= 300; i++) { bool delta = false; for(int j = 1; j <= n; j++) if (f[j] >= 0) { int x = j - a[j]; if (x >= 1 && (f[x] == -1 || f[x] > f[j] + 1)) { delta = true; f[x] = f[j] + 1; } x = j + a[j]; if (x <= n && (f[x] == -1 || f[x] > f[j] + 1)) { delta = true; f[x] = f[j] + 1; } } if (!delta) break; } printf("%d\n", f[t]); } return 0;}
总结
今天get到并查集的新操作,下午做题状态不是很好,但是晚上自己在一个陌生的环境里,状态好的出奇,昨晚打的CF掉分了,。。。D题竟然爆掉了int, 难过希望自己在暑期集训收货更大吧
- 2017暑期集训 Day 3 搜索与并查集
- 2017暑期集训 Day 3
- 2017暑期集训Day 9 递推
- 2017暑期集训Day 11 背包
- 2017暑期集训Day 14 树形dp
- 2017暑期集训Day 25 树状数组
- sduacm2016级暑假集训 搜索&并查集
- 2017暑期集训Day 14 区间dp+二分图匹配
- 暑期集训第二周---搜索
- 2017暑假集训 div1 并查集(1)
- 2017暑假集训 div1 并查集(2)
- 北大暑期学习之并查集
- 陕西省集训(并查集)
- 暑期集训搜索专题(一)
- 暑期集训搜索专题(一)
- 2016暑期集训---搜索(整数拆分)
- 2016暑期集训---搜索(简单BFS)
- 2016暑期集训---搜索(简单BFS)
- python sqlite3
- java技术之静态代理
- 冒泡排序
- Hadoop发行版
- 如何判断并求出一个整数x是否可以表示成n(n>=2)个连续正整数的和。
- 2017暑期集训 Day 3 搜索与并查集
- USACO-section1.5 Prime Palindromes[回文数][素数]
- 创建WebP图片|Android Studio 谷歌图像文件格式
- 公众号“今日作文素材”文章列表 (20170715-20170721)
- 未在此计算机上注册ActiveX控件"{648A5600-2C6E-101B-82B6-000000000014}"
- 【HNOI2016模拟4.4】Fenwit
- 为何mfc使用MAKEINTRESOURCE
- Web接口测试工具--Jmeter
- 图论500题