2017暑期集训 Day 3 搜索与并查集
2017-07-14 22:32
363 查看
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 ; 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 ; int n, m; bool rep ; double x , y ; 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 ; int a [5], n, m, tot ; 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 ; int s , t , 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 , b ; 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 , dp , par ; bool f ; vector<int> a ; vector<int> tree ; 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 , dis , tle[100]; int n, s, t; bool f ; 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 , a ; 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 14 区间dp+二分图匹配
- 2017暑期集训Day 9 递推
- 2017暑期集训Day 25 树状数组
- 2017暑期集训Day 11 背包
- 2017暑期集训 Day 3
- 暑期集训第二周---搜索
- 2017暑假集训 div1 并查集(2)
- 2016-2017 HPU暑期集训练习赛
- sduacm2016级暑假集训 搜索&并查集
- 【集训】DP & 搜索 & 线段树 Day 2
- 2016暑期集训---搜索(BFS 八方向马步问题)
- 15/7/2017 暑期第一次集训小总结
- 2010暑期集训第二专题(搜索)关于搜索的一点两点三点...
- 2017暑期集训——Wet Shark and Bishops(思维)
- 2017暑假集训 div1 简单搜索
- 2016暑期集训---搜索(简单BFS+路径储存)
- 暑期集训搜索专题(一)
- 2016暑期集训---搜索(整数拆分)
- 集训第三天(2017/8/2):继续刷搜索题
- 「雅礼集训 2017 Day2」水箱 并查集+树形DP