Codeforces Round #417 (Div. 2)——ABCE
题面有点长需耐心读题。
A.一个人行道上的人被撞有4种情况
1.所在车道有车驶出
2.右边车道有左转车
3.左边车道有右转车
4.对面车道有直行车
#include <bits/stdc++.h> #define rep(i, j, k) for(int i = j;i <= k;i ++) #define rev(i, j, k) for(int i = j;i >= k;i --) using namespace std; typedef long long ll; int a[4][4]; int main() { ios::sync_with_stdio(false); rep(i, 0, 3) rep(j, 0, 3) cin >> a[i][j]; rep(i, 0, 3) { if(a[i][3]) { rep(j, 0, 2) if(a[i][j]) { puts("YES"); return 0; } if(a[(i + 1) % 4][0] | a[(i + 2) % 4][1] | a[(i + 3) % 4][2]) { puts("YES"); return 0; } } } puts("NO"); return 0; }
B.从下往上,每层楼有两种选择——
左边结束 or 右边结束
所以有 2^n 种决策
坑点( Pretest 里有):如果第 k + 1 层到第 n 层楼都已关灯
那么到第 k 层楼结束就好了
#include <bits/stdc++.h> #define rep(i, j, k) for(int i = j;i <= k;i ++) #define rev(i, j, k) for(int i = j;i >= k;i --) using namespace std; typedef long long ll; int n, m, ans = 666666666; char s[20][120]; void dfs(int k, int d, int c) { if(k >= n) { if(d) { int t = m; rev(i, m - 1, 1) if(s[k][i] == '1') t = i; ans = min(ans, c + m - t); } else { int t = 1; rep(i, 2, m) if(s[k][i] == '1') t = i; ans = min(ans, c + t - 1); } } else { if(d) { int t = m; rev(i, m - 1, 1) if(s[k][i] == '1') t = i; dfs(k + 1, 1, c + 2 * (m - t) + 1); dfs(k + 1, 0, c + m); } else { int t = 1; rep(i, 2, m) if(s[k][i] == '1') t = i; dfs(k + 1, 0, c + 2 * (t - 1) + 1); dfs(k + 1, 1, c + m); } } } int main() { ios::sync_with_stdio(false); cin >> n >> m, m += 2; rep(i, 1, n) cin >> (s[n + 1 - i] + 1); rev(i, n, 1) { int flag = 1; rep(j, 1, m) { if(s[i][j] == '1') { flag = 0; break; } } if(flag) n --; else break; } if(!n) cout << "0"; else dfs(1, 0, 0), cout << ans; return 0; }
C.显然 T 随 k 单调递增
所以直接二分 k 就好了 (蠢了多写了个cmp
O(nlog^2n) cf 的评测机还是很稳的
#include <bits/stdc++.h> #define rep(i, j, k) for(int i = j;i <= k;i ++) #define rev(i, j, k) for(int i = j;i >= k;i --) using namespace std; typedef long long ll; int n, m, ans = 666666666; char s[20][120]; void dfs(int k, int d, int c) { if(k >= n) { if(d) { int t = m; rev(i, m - 1, 1) if(s[k][i] == '1') t = i; ans = min(ans, c + m - t); } else { int t = 1; rep(i, 2, m) if(s[k][i] == '1') t = i; ans = min(ans, c + t - 1); } } else { if(d) { int t = m; rev(i, m - 1, 1) if(s[k][i] == '1') t = i; dfs(k + 1, 1, c + 2 * (m - t) + 1); dfs(k + 1, 0, c + m); } else { int t = 1; rep(i, 2, m) if(s[k][i] == '1') t = i; dfs(k + 1, 0, c + 2 * (t - 1) + 1); dfs(k + 1, 1, c + m); } } } int main() { ios::sync_with_stdio(false); cin >> n >> m, m += 2; rep(i, 1, n) cin >> (s[n + 1 - i] + 1); rev(i, n, 1) { int flag = 1; rep(j, 1, m) { if(s[i][j] == '1') { flag = 0; break; } } if(flag) n --; else break; } if(!n) cout << "0"; else dfs(1, 0, 0), cout << ans; return 0; }
E.(题解说的比较清楚了其实,学习了已一发题解)
我们很熟悉最基础的求异或和的Nim游戏
现在我们添加一条规则,对于当前将要操作的玩家
他任选一堆除了拿走正数数量的石子外
他还可以在这堆上添加正数数量的石子
对于这个变种游戏,可以考虑它可以直接转化为最基础的Nim游戏
因为原来的胜者在对方选择添加石子的操作后
他可以再把对方添加的石子拿走...
各操作一轮下来相当于啥也没干...
把这些啥也没干的过程去掉就是原始的Nim游戏
我们考虑这个E题
把所有叶子节点染成蓝色
蓝点的父亲染红,红点的父亲染蓝
(为防止矛盾所以保证了所有叶节点与树根距离同奇偶)
我们可以发现,蓝点就是我们的Nim游戏
而红点可以传递苹果给蓝点
就是我们上面新加的不影响结果的规则
(也可以考虑为,红点的苹果传递到叶子节点需要奇数次传递
然后再被后手玩家一次吃光,总共经历了偶数步,不影响结果
而对于蓝色的叶子节点,就只有被拿走的选择
蓝色的非叶子节点,传递给下面的红点等价于被拿走,因为那不再影响结果
所以这已经是Nim游戏,每个蓝色节点即为一堆)
这样我们计算蓝点的异或和 sum
sum = 0,初始局面后手稳赢
那他就随便蓝蓝互换,红红互换
红蓝互换当且仅当两个节点苹果数相同
sum != 0,需交换蓝 u 和红 v
当且仅当 sum ^ apple[u] ^ apple[v] = 0
#include <bits/stdc++.h> #define rep(i, j, k) for(int i = j;i < (k + 1);i ++) using namespace std; long long ans, c0, c1; int n, md, ret, d[100010], a[100010], f[100010], c[10000010]; vector <int> e[100010]; void dfs(int u, int dep) { d[u] = dep, md = max(md, dep); rep(i, 0, e[u].size() - 1) dfs(e[u][i], dep + 1); } int main() { ios::sync_with_stdio(false); cin >> n; rep(i, 1, n) cin >> a[i]; rep(i, 2, n) cin >> f[i], e[f[i]].push_back(i); dfs(1, 1); rep(i, 1, n) { if((md - d[i]) & 1) c1 ++; else c0 ++, c[a[i]] ++, ret ^= a[i]; } if(ret) { rep(i, 1, n) if(((md - d[i]) & 1) && (ret ^ a[i]) <= 10000000) ans += c[ret ^ a[i]]; } else { ans = c0 * (c0 - 1) + c1 * (c1 - 1), ans >>= 1; rep(i, 1, n) if((md - d[i]) & 1) ans += c[a[i]]; } cout << ans; return 0; }