这场貌似是因为贪玩就没打了。
vp了一下发现正好是我薄弱的部分,没打有点可惜。
A (+1)
WA2:
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e6 + 10; int n, m, k; int a[maxn]; int main() { int T; scanf("%d", &T); while (T--) { int n; scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); } sort(a+1, a+1+n, greater<int>()); int base = m / (a[1] + a[2]) ; m -= (a[1] + a[2]) * base; base *= 2; while (m) { m -= a[1]; base ++; if (m <= 0) break; m -= a[2]; ///主要是忽略了这里-a[2]会让 m < 0 之后 while (m)其实会把负数再次进入导致答案多1 base ++; } cout << base << endl; } return 0; }
AC:
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e6 + 10; int n, m, k; int a[maxn]; int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); } sort(a+1, a+1+n, greater<int>()); int base = m / (a[1] + a[2]) ; m -= (a[1] + a[2]) * base; base *= 2; if (m > 0) { m -= a[1]; base ++; } if (m > 0) { m -= a[2]; base ++; } cout << base << endl; } return 0; }
B (+3)
主要这题看上去我一点感觉都没有,还是脑子不大够吧。
假设n = 5。
不如先假设x = 1吧,意味着所有数字都可以和其他位置任意一个数进行交换。
之后再假设x = 2,发现第一个数字可以和3 到 5位置上的数字进行交换,之后又发现第二个位置可以和4到5位置进行交换。
这两个位置可以合并,就相当于第一个位置可以和2-5进行交换(2可以先换到4或者5,之后1跟4或者5进行交换就可以获得2的位置了)
之后其他位置同理,我们发现x = 2的时候也是可以全部交换的。
之后我们在考虑x = 3,发现第三个数字无论如何都不能被交换(3-3 < 1, 3+3 > 5)。
于是我们得到了一个想法,即只要不能交换的位置跟目的数组是相同的,那么我们就可以操作成功。否则就会失败。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e6 + 10; int n, x; int a[maxn], b[maxn]; int vis[maxn]; int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &x); for (int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); b[i] = a[i]; vis[i] = 0; } sort(b+1, b+1+n); for (int i = 1+x; i <= n; ++ i) { vis[i] = vis[i-x] = 1; } int flag = 0; for (int i = 1; i <= n; ++ i) { if (vis[i] == 0 && a[i] != b[i]) flag = 1; } if (flag == 1) puts("NO"); else puts("YES"); } return 0; }
C
这道题在vp的时候并没有做出来。
我已经到了如果所有数的xorsum为0的话怎么YES,
如果不为0的话,需要找到三个部分使得这三个部分的xorsum都为整个数组的xorsum。
被卡在如何找到三个部分。之后看了眼别人的题解,其实并不是很难。主要还是没想到吧。
其实我觉得我应该算式想到一点点了。我的想法是从叶子节点出发,一直向上xor,如果找到xorsum为目标的话,那么就cnt++;
但是这样其实是有一个问题的,就是说叶子节点一直向上的话,如果走到了分岔路,你不知道应该要往哪里走。这样的话有可能走完整棵树才会找到目标值。
于是我就觉得这个想法非常的难实现,或者说就是错的。
题解的做法其实就是一个树形dp。思想是根节点出发,先到底部(叶子节点),向上异或,一旦找到值就向上返回0,相当于是吧下面的这个连通块给截断了。之后cnt++,最后统计cnt的值是否大于2就好了。
并不是特别难写,主要还是有一点思维在里面的。你要想到,如果一个树被分成很多个部分的话,这些部分一定会分别有一条或者多条从叶子节点出发的链。这样的话就保证这个树形DP是正确的了。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e6 + 10; int a[maxn]; int xorsum; int cnt = 0; vector<int> G[maxn]; int DFS(int u, int fa, int sum) { //cout << u << " " << fa << endl; int tsum = sum; for (auto v : G[u]) { if (v == fa) continue; sum ^= DFS(v, u, tsum); } sum ^= a[u]; if (sum == xorsum) { cnt ++; return 0; } return sum; } int main() { int T; scanf("%d", &T); while (T--) { int n, k; scanf("%d%d", &n, &k); xorsum = cnt = 0; for (int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); G[i].clear(); xorsum ^= a[i]; } for (int i = 1; i <= n-1; ++ i) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } if (xorsum == 0) { puts("YES"); } else if (k > 2) { DFS(1, 0, 0); if (cnt > 2) { puts("YES"); } else puts("NO"); } else puts("NO"); } return 0; }
D.
是一个比较有趣的交互题。最后还是看了题解。
不过还是记录一下自己的思考过程吧。
一定会询问一次所有的点来找到最大值(废话)
首先我发现如果是求点对经过边的gcd最大,那么其实就是求树上最大的边权是哪条。
之后很容易想到一个O(n)的询问,即询问每一条边。
之后我一直在想如何优化这个询问的过程。
先考虑为什么是求gcd呢?假设问的是瓶颈路呢?路径的gcd是否有什么关键的性质?(最后发现唯一的性质就是让我知道题目其实是求树上最大边权)
之后想了一个假算法,大概就是每次以根节点出发,每次查询一般的子树,如果不在这一半里就在另一半里,之后继续二分下去,知道找到那一条边。
为啥我觉得他是假的呢,因为最后求的其实是两个点,但是这个算法吧求的其实是一条路径,之后这条边只是在这条路径上,而且这个算法的询问次数大概是 高度 * log(n/高度)我觉得肯定过不去。
看了题解发现其实只是需要一个工具而已,欧拉序(其实之前就用过这个东西了,但是一直不知道他叫这个)
我们把每个节点入栈的时间与出栈的时间都记录下来,并排成一个序列,我们发现每棵子树的序列都是连续的。如果我们二分这个区间,那么我们就相当于是二分每一个子树,所以我们一定能求得这条边。
注意欧拉序中节点是会被重复加入的。所以我们需要询问的时候去重。
(话说这个二分其实我也不是很会写。好菜啊,之前我写的二分大多都是懒人版的二分,就是单点查询,但是现在需要的其实是一个长度为2的区间,于是我的懒人版二分就失效了,主要还是自己对二分的理解不大够吧,打算之后看看课好好再学一次二分)
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e6 + 10; int n, vis[maxn]; vector<int> G[maxn]; int euler[maxn], tot; void DFS(int u, int fa) { euler[++tot] = u; for (auto v : G[u]) { if (v == fa) continue; DFS(v, u); euler[++tot] = u; } } int query(int L, int R) { vector<int> out; for (int i = L; i <= R; ++ i) { if (vis[euler[i]] == 0) { out.push_back(euler[i]); vis[euler[i]] = 1; } } cout << "? " << out.size(); for (auto u : out) { cout << " " << u; vis[u] = 0; } cout << endl; int get; cin >> get; return get; } int main() { int n; cin >> n; for (int i = 1; i <= n-1; ++ i) { int u, v; cin >> u >> v; G[u].push_back(v); G[v].push_back(u); } DFS(1, 0); int L = 1, R = tot, rec int maxx = query(1, tot); while (L < R-1) { int mid = L + R >> 1; if (query(L, mid) == maxx) { R = mid; } else { L = mid; } } cout << "! " << euler[L] << " " << euler[R] << endl; return 0; }
E
一开始我一直在往a ^ b = a + b - (a&b << 1)上去想,即异或等于非进位加法。
于是这道题就卡死了。其实就是异或操作的奇偶性,我们发现每一位上如果是偶数个一那么就是0,奇数个1就是1。
那么如果这个区间长度为奇数,那么就不可能能满足题目要求。因为最高位个数都是1,然后又是奇数个,所以左右两边都是会有最高位。
其他位也是同理。
于是我们从奇偶性出发。
首先我们考虑a & b > a ^ b,假设最高位为k,当且仅当a,b在k位都有1,并且a^b在比k大的数位上没有1。
于是我们考虑从高到低枚举每一位的贡献。并且记录前k位的前缀异或和,当前缀异或和为0的时候,找到最小的位置,检查最小的位置到当前位置是否全部都是1即可,有点类似双指针的思想。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e6 + 10; int n; int a[maxn], cnt[maxn], sum[maxn], pos[maxn], vis[maxn]; int main() { scanf("%d", &n); for (int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); } int ans = 0; for (int j = 20; j >= 0; j --) { for (int i = 1; i <= n; ++ i) { cnt[i] = cnt[i-1] + ((a[i] >> j) & 1); sum[i] = sum[i-1] ^ (a[i] >> j); } memset(vis, -1, sizeof vis); vis[0] = 0; for (int i = 1; i <= n; ++ i) { if (vis[sum[i]] != -1) { if (cnt[i] - cnt[vis[sum[i]]] == i - vis[sum[i]]) { ans = max(ans, i - vis[sum[i]]); } else vis[sum[i]] = i; } else vis[sum[i]] = i; } } printf("%d ", ans); return 0; }