May Cook-Off 2019 解题报告
太气了。Atcoder unrated了。
这一场时间太不友好了。昨天下午一时兴起就去补了一发。题很好,学到好多东西。
题意:给一个矩阵,这个矩阵是稳定的当且仅当每一个元素都是稳定的。元素是稳定的当且仅当它的值严格小于它相邻有几个元素
思路:四个角的值就必须小于2,然后外圈的其他数的值就必须小于3,其他的就必须小于4。
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 20; int a[N][N]; int main() { int T = read(); while (T--) { int r = read(), c = read(); bool ans = false; for (int i = 1; i <= r; i++) { for (int j = 1; j <= c; j++) { a[i][j] = read(); if (i == 1) { if (i == j || j == c) { if (a[i][j] >= 2) ans = true; } else { if (a[i][j] >= 3) ans = true; } } else if (i == r) { if (1 == j || j == c) { if (a[i][j] >= 2) ans = true; } else { if (a[i][j] >= 3) ans = true; } } else if (j == 1 || j == c) { if (a[i][j] >= 3) ans = true; } else { if (a[i][j] == 4) ans = true; } } } if (ans) puts("Unstable"); else puts("Stable"); } }
题意:一个等差数列,其中一个数被改了,要求原数列。
思路:四项肯定可以出一个等差数列,只看前四项就ok了。
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 1e5 + 10; int a[N], b[N]; int main() { int T = read(); while (T--) { int n = read(); for (int i = 1; i <= n; i++) a[i] = read(); if (a[2] - a[1] == a[3] - a[2]) { int d = a[2] - a[1]; for (int i = 2; i <= n; i++) { a[i] = a[i-1] + d; } } else if (a[3] - a[2] == a[4] - a[3]) { int d = a[3] - a[2]; for (int i = n - 1; i > 0; i--) { a[i] = a[i+1] - d; } } else { int d = (a[4] - a[1]) / 3; for (int i = 2; i <= n; i++) { a[i] = a[i - 1] + d; } } for (int i = 1; i <= n; i++) { if (i - 1) putchar(' '); printf("%d", a[i]); } puts(""); } return 0; }
题意:给一个区间$l,r$和一个数$g$,要求这个区间内最多有几个数的GCD刚好为$g$
思路:容斥求,WA了好几发,原因是。当容斥出来结果只有1个时,$g$必须在$l,r$之间,不然这一个数的最大因子是自己而不是$g$。
题目描述也是The greatest positive integer which divides each element of the set is exactly $g$,一个数的greatest positive integer which divide it是它本身。
#include <bits/stdc++.h> #define ll long long using namespace std; inline ll readl() { ll x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } int main() { ll T = readl(); while (T--) { ll l = readl(), r = readl(), g = readl(); ll ans = r / g - l / g + (l % g == 0); if (ans == 1) { if (g < l || g > r) ans = 0; } printf("%lld\n", ans); } return 0; }
题意:给一个有向图,问能不能删边删点使他们成为一个二分图 一边点数为2,一边点数为$k$
思路:暴力求。不知道为啥不会T。咱也不知道,咱也不敢问。
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 2e3 + 10; vector<int> G[N]; int main() { int n = read(), m = read(), k = read(); for (int i = 0; i < m; i++) { int u = read(), v = read(); G[u].emplace_back(v); G[v].emplace_back(u); } for (int i = 1; i <= n; i++) sort(G[i].begin(), G[i].end()); for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { int p1 = 0, p2 = 0; int ans = 0; int sz1 = G[i].size(), sz2 = G[j].size(); while (p1 < sz1 && p2 < sz2) { if (G[i][p1] == G[j][p2]) { p1++;p2++; ans++; } else if (G[i][p1] < G[j][p2]) { p1++; } else p2++; } if (ans >= k) { puts("YES"); return 0;} } } puts("NO"); return 0; }
看了题解的解法。复杂度就比较科学了。$O\left( n^{2}k\right)$
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 2e3 + 10; vector<int> G[N]; int cnt[N][N]; int main() { int n = read(), m = read(), k = read(); for (int i = 0; i < m; i++) { int u = read(), v = read(); G[u].emplace_back(v); G[v].emplace_back(u); } bool ans = false; for (int i = 1; i <= n; i++) { if (ans) break; for (auto x: G[i]) { if (ans) break; for (auto y: G[i]) { if (x < y) { cnt[x][y]++; cnt[y][x]++; } if (cnt[x][y] >= k) { ans = true; break; } } } } if (ans) puts("YES"); else puts("NO"); return 0; }
题意:给n个物品,每个物品有值$b_{i}$和选中概率$p_{i}$,求异或和的期望
思路:emmmm概率题本来就不太会再加上异或emmmmm
按位考虑每一个数,因为异或是两个相同就变成0了。那么能对期望做出贡献的时候就是二选一的时候
pro表示在当前位,当前数能对期望做出贡献的概率。
到下一个数的时候就是$pro\ast \left( 1-p_{i}\right) + p_{i}\ast \left( 1-pro\right)$
最后乘上(1 << k) 就行了 k表示当前位
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 1e5 + 10; int b[N], n; double p[N]; int main() { int T; scanf("%d", &T); while (T--) { scanf("%d", &n); for (int i = 0; i < n; i++) scanf("%d", &b[i]); for (int i = 0; i < n; i++) scanf("%lf", &p[i]); long double ans = 0; for (int bit = 0; bit < 32; bit++) { long double pro = 0; for (int i = 0; i < n; i++) { if ((b[i] >> bit) & 1) { pro = (1 - pro) * (long double)p[i] + pro * (1 - (long double)p[i]); } } ans += (1 << bit) * pro; } printf("%.7Lf\n", ans); } return 0; }
题意:有节点0到$2^{k} - 1$,给一个序列$m_{i}$ ,所有点对$u,v$ $u\oplus v = m_{i}$的之间连边,问最后有多少个连通块
思路:推了半天结论也没推明白。看题解才明白是高斯消元。
对于一个节点$u$,它能到达的点是这个序列的数,任意组合,异或之后的值的个数(加上一个0)
本来是只能异或这个序列里面的一个数,但是由于别的数也会异或,就会连通了。
比如两个节点1 和 2
$m_{1} = 1$ $m_{2} = 2$
1和$m_{1}$异或后是0 和$m_{2}$异或后是3 2和$m_{1}$异或后是3
本来只异或一个值的话,1和2看起来是不连通的
但就是会间接相连 所以就是xjb组合着去异或
然后答案就是看这个序列能异或出多少种值来。
把它们按二进制排成一个矩阵。
求出这个矩阵的秩$r$
答案就是$2^{k - r}$
解释:把矩阵通过异或运算变成行阶梯型之后,秩为$r$,前$r$行的线性组合得到的结果必是不一样了
然后取和不取种类数就是$2^{r}$种
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 1e5 + 10; int a[N]; int bin[N]; int main() { int T = read(); while (T--) { memset(bin, 0, sizeof(bin)); int k = read(), m = read(); for (int i = 0; i < m; i++) { int x = read(); for (int j = 0; j < 31; j++) { if ((1<<j) & x) { if (!bin[j]) { bin[j] = x; k--; break; } x ^= bin[j]; } } } cout << (1 << k) << '\n'; } return 0; }
题意:一棵树,刚开始只有根1和颜色$c$,给$Q$次询问,询问有两种,第一种是给节点$u$加上一个儿子(编号为当前节点数+1),颜色染成$c$,第二种询问颜色为$c$的节点中,和$u$最近的距离是多少。
思路:直接写了发暴力找+倍增LCA,T了。看了题解才发现有trick。
把颜色分为大和小。以300次为界限。
出现次数小于300的就可以直接暴力+LCA
大于300的把颜色为$c$的节点$u$ $dis[c][u] = 0$并且对这些节点进行bfs
bfs过程中若dis为INF的话就可以更新成$dis[c][u] + 1$
#include <bits/stdc++.h> using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); } return x * f; } const int N = 2e5 + 10; const int RT = 300; const int INF = 0x3f3f3f3f; vector<int> group[N]; vector<int> rem[N]; vector<int> big; vector<int> adj[N]; vector<int> dis[N]; int fa[N], lca[N][25], n, dep[N], q, col[N]; void recalc(int c) { if (dis[c].empty()) big.emplace_back(c); dis[c].assign(n + 1, N); queue<int> que; for (int u: group[c]) { dis[c][u] = 0; que.push(u); } while (!que.empty()) { int u = que.front(); que.pop(); for (int v: adj[u]) { if (dis[c][v] == N) { dis[c][v] = dis[c][u] + 1; que.push(v); } } } rem[c].clear(); } void add(int u, int c) { n++; col[n] = c; lca[n][0] = fa[n] = u; adj[n].emplace_back(u); adj[u].emplace_back(n); dep[n] = dep[u] + 1; for (int i = 1; i <= 20; i++) lca[n][i] = lca[lca[n][i-1]][i-1]; for (int cc: big) dis[cc].emplace_back(dis[cc][u] + 1); group[c].emplace_back(n); rem[c].emplace_back(n); if (rem[c].size() >= RT) recalc(c); } int Lca(int u, int v) { if (dep[u] < dep[v]) swap(u, v); int f = dep[u] - dep[v]; for (int i = 0; i <= 20; i++) { if (f & (1 << i)) { u = lca[u][i]; } } if (u == v) return v; for (int i = 20; i >= 0; i--) { if (lca[u][i] != lca[v][i]) { u = lca[u][i]; v = lca[v][i]; } } return lca[u][0]; } int distance(int u, int v) { return dep[u] + dep[v] - 2 * dep[Lca(u, v)]; } int query(int u, int c) { int ans = dis[c].size() ? dis[c][u] : N; for (int v: rem[c]) ans = min(ans, distance(u, v)); if (ans == N) ans = -1; return ans; } void init() { for (int i = 0; i < N; i++) { adj[i].clear(); group[i].clear(); dis[i].clear(); rem[i].clear(); } big.clear(); n = 1; } int main() { int T = read(); while (T--) { init(); q = read();int c = read(); group[c].emplace_back(1); rem[c].emplace_back(1); dep[1] = 0; fa[1] = 1; int a = -1; while (q--) { char s[10]; scanf("%s", s); int u = read(); c = read(); u = u ^ (a + 1); c = c ^ (a + 1); if (s[0] == '+') add(u, c); else printf("%d\n", a = query(u, c)); } } return 0; }
还是太菜了呀...

浙公网安备 33010602011771号