根据我的主观感受排序
H:蒟蒻wzc的填数游戏
知识点:模拟暴力
模拟就完事了
#include <bits/stdc++.h> using namespace std; char s[30000]; int g[10][10]; int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; inline void solve() { memset(g, 0, sizeof g); int n; cin >> n; unordered_map<char, int> f; f['w'] = 0, f['d'] = 1, f['s'] = 2, f['a'] = 3; scanf("%s", s + 1); int cnt = 1; g[1][1] = 1; int x = 1, y = 1; for (int i = 1; s[i]; i ++ ) { int tmp = f[s[i]]; x = x + dx[tmp], y = y + dy[tmp]; g[x][y] = ++ cnt; } for (int i = 1; i <= n; i ++ ) { for (int j = 1; j <= n; j ++ ) cout << g[i][j] << ' '; cout << endl; } cout << endl; } int main() { int t; cin >> t; while (t -- ) { solve(); } return 0; }
A:uu与糖果
知识点:模拟暴力
模拟就完事了,注意一下h为0的特判即可
#include <bits/stdc++.h> using namespace std; const int N = 2000020; typedef long long LL; LL a[N]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr); LL n, k, h; cin >> n >> k >> h; LL res = 0; if (h == 0) { for (int i = 1; i <= n; i ++ ) cin >> a[i], res += a[i]; cout << res << endl; } else { for (int i = 1; i <= n; i ++ ) { cin >> a[i]; if (a[i] > k) res += a[i]; else { LL re = k - a[i]; a[i] += re / h * h; res += a[i]; } } res += h; cout << res << endl; } return 0; }
F:签不上到?做这题就对了
知识点:模拟暴力
将数中所有数位拆出来,然后从高位到低位遍历,如果之后有比当前大的,就取最大的最后的那个数字交换即可
其实只要判断大于0就行了,这里是考虑全了的写法
#include <bits/stdc++.h> using namespace std; typedef long long LL; inline void solve() { LL n; cin >> n; LL tmp = n; vector<int> v; if (tmp == 0) { cout << 0 << endl; } else if (tmp > 0) { while (tmp) v.push_back(tmp % 10), tmp /= 10; reverse(v.begin(), v.end()); for (int i = 0; i < v.size(); i ++ ) { int ma = v[i], poi = i; for (int j = i; j < v.size(); j ++ ) { if (ma <= v[j] && v[j] != v[i]) { ma = v[j]; poi = j; } } if (poi == i) continue; else { swap(v[poi], v[i]); break; } } for (auto ite : v) cout << ite; cout << endl; } else { tmp = -tmp; while (tmp) v.push_back(tmp % 10), tmp /= 10; reverse(v.begin(), v.end()); for (int i = 0; i < v.size(); i ++ ) { int mi = v[i], poi = i; for (int j = i; j < v.size(); j ++ ) { if (mi >= v[j] && v[j] != v[i]) { mi = v[j]; poi = j; } } if (poi == i) continue; else { swap(v[poi], v[i]); break; } } cout << '-'; bool is = false; for (auto ite : v) { if (ite == 0 && is == false) { is = true; continue; } else cout << ite; } cout << endl; } } int main() { int t; cin >> t; while (t -- ) { solve(); } return 0; }
B:蒟蒻wzc与方块涂色
知识点:二进制搜索
CF有一道差不多的题目:B. Berland Crossword
CF上是平面,这里是三维,其实是一个道理,我们为了讲解方便,拿一个平面来举例子,其他的面同理
设一个面的四条边分别要涂a,b,c,d块黑色,我们很容易发现拐角的那个方块比较难处理,于是我们直接二进制搜索,用一个四位的二进制数的每一位来表示是否要填,填完后我们验证即可
所以当每条边减去角上的点过后应该大于等于0并且小于等于n - 2,否则不合法,检查下一状态是否合法
这是平面状态下的,如果是立体的,就一个一个面判断即可
一共8个顶点,从枚举到
一共
的复杂度,加上1000组数据,不会超时
#include <bits/stdc++.h> using namespace std; int a[20]; int n; inline bool change(int p1, int p2, int p3, int p4, int l1, int l2, int l3, int l4) { if (p1 == 1) l1 -- , l4 -- ; if (p2 == 1) l1 -- , l2 -- ; if (p3 == 1) l2 -- , l3 -- ; if (p4 == 1) l3 -- , l4 -- ; if (l1 < 0 || l2 < 0 || l3 < 0 || l4 < 0) return false; if (l1 > n - 2 || l2 > n - 2 || l3 > n - 2 || l4 > n - 2) return false; return true; } inline bool check(int x) { vector<int> v; v.push_back(123); for (int i = 0; i < 8; i++) v.push_back(x >> i & 1); //第一面 if (!change(v[1], v[2], v[3], v[4], a[1], a[2], a[3], a[4])) return false; //第二面 if (!change(v[1], v[8], v[5], v[4], a[7], a[6], a[5], a[4])) return false; //第三面 if (!change(v[8], v[7], v[6], v[5], a[8], a[9], a[10], a[6])) return false; //第四面 if (!change(v[2], v[7], v[6], v[3], a[11], a[9], a[12], a[2])) return false; //第五面 if (!change(v[8], v[7], v[2], v[1], a[8], a[11], a[1], a[7])) return false; //第六面 if (!change(v[5], v[6], v[3], v[4], a[10], a[12], a[3], a[5])) return false; return true; } inline void solve() { cin >> n; for (int i = 1; i <= 12; i ++ ) cin >> a[i]; for (int i = 0; i < (1 << 8); i ++ ) { if (check(i)) { puts("YES"); return ; } } puts("NO"); } int main() { int t; cin >> t; while (t -- ) solve(); return 0; }
#include <iostream> using namespace std; int a[120], b[120]; int main() { int t; cin >> t; while(t--) { int n, k, x; int the_max = 0; cin >> n >> k >> x; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n - 1; i ++) { if(!(a[i] == a[i + 1] && a[i] == x)) continue; //cout << i << 'c' << endl; for(int j = 1; j <= i; j++) { b[j] = a[j]; } b[i + 1] = x; for(int j = i + 2; j <= n + 1; j++) { b[j] = a[j - 1]; } b[n + 2] = 0; //for(int i = 1; i <= n + 1; i++) cout << b[i] << ' ';cout << endl; int head = i, tail = i + 2, cnt = 0, col = x, col1 = x, col2 = x, flag = 1; while(head >= 1 && tail <= n + 1) { //cout << col1 << ' ' << col2 << endl; if(col1 != col2) break; //cout << col1 << ' ' << col2 << endl; col = col1; head--, tail++; while(b[head] == col && head >= 1) { cnt++; head--; flag = 1; //cout << 'b' << '+' << endl; } col1 = b[head]; while(b[tail] == col && tail <= n + 1) { cnt++; tail++; flag = 1; //cout << 'c' << '+' << endl; } col2 = b[tail]; if(flag == 1) cnt += 2; //cout << "+" << endl; else break; flag = 0; } //cout << cnt << endl; if(cnt > the_max) the_max = cnt; } cout << the_max << endl; } }
I:集训队长魂牵梦绕的题目
知识点:模拟
这题在CF上也有一个差不多的:B. Minimal Cost
不过思想很简单:我们把可以阻挡去路的两个点组叫做障碍,我们只需要在每次修改过后查看障碍个数即可
#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 20; int a[N], b[N]; int main() { ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr); int n; cin >> n; int cnt = 0;//记录障碍个数 for (int i = 1; i <= n; i ++ ) cin >> a[i]; for (int i = 1; i <= n; i ++ ) cin >> b[i]; for (int i = 2; i <= n; i ++ ) { if (a[i] == 0) continue; if (b[i - 1] == 1) cnt ++ ; if (b[i] == 1) cnt ++ ; if (b[i + 1] == 1) cnt ++ ; } int q; cin >> q; if (cnt == 0) cout << "I'm the worst friend.Please forgive me." << endl; else cout << "You are the worst friend." << endl; while (q -- ) { int x, y; cin >> x >> y; if (x == 1) { if (a[y] == 1) { if (b[y] == 1) cnt -- ; if (b[y - 1] == 1) cnt -- ; if (b[y + 1] == 1) cnt -- ; a[y] = 0; } else { if (b[y] == 1) cnt ++ ; if (b[y - 1] == 1) cnt ++ ; if (b[y + 1] == 1) cnt ++ ; a[y] = 1; } } else { if (b[y] == 1) { if (a[y] == 1) cnt -- ; if (a[y - 1] == 1) cnt -- ; if (a[y + 1] == 1) cnt -- ; b[y] = 0; } else { if (a[y] == 1) cnt ++ ; if (a[y - 1] == 1) cnt ++ ; if (a[y + 1] == 1) cnt ++ ; b[y] = 1; } } if (cnt == 0) cout << "I'm the worst friend.Please forgive me." << endl; else cout << "You are the worst friend." << endl; } return 0; }
G:来上数学课
知识点:贪心
我们通过自己模拟可以发现只要在它即将加倍的那一题让他答错,并且是在尽量后面的题目中答错即可,知道了这个策略我们写出代码也不难了
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int mod = 1e9 + 9; inline LL q_pow(LL a, LL b) { LL ans = 1; for (; b; b >>= 1) { if (b & 1) ans = ans * a % mod; a = a * a % mod; } return ans; } int main() { int n, m, k; while (cin >> n >> m >> k) { LL cnt = n / k; if (cnt <= n - m) cout << m << endl; else { LL tmp = cnt - n + m; LL ans = k * (q_pow(2, tmp + 1) - 2 + mod) % mod; ans = (ans + m - (k * (cnt - n + m))) % mod; cout << ans << endl; } } return 0; }
C:排列组合之王
知识点:位运算
位运算中我们只需要计算每一个二进制数位上的贡献即可,因此我们用一个数m来记下每位是否有出现1,因为有1就有贡献,没有1的话永远也不会有1
最后通过打表发现在这样的情况下每位的贡献就是,暂时不知道如何证明(蹲一个大佬)
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int mmod = 1e9 + 7; inline LL q_pow(LL a, LL b, int mod) { LL ans = 1; for (; b; b >>= 1) { if (b & 1) ans = ans * a % mod; a = a * a % mod; } return ans; } int main() { int n; cin >> n; LL m = 0; for (int i = 1; i <= n; i ++ ) { int x; cin >> x; m |= x; } LL pp = q_pow(2, n - 1, mmod); cout << m * pp % mmod; return 0; }
D:异或游戏
知识点:Trie,前缀和
就是个板子题,然后用前缀和来维护前缀的最小值,后缀和来维护后缀的最大值(这里的前缀后缀和都指异或和)
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 6000060; struct node { LL cnt = 0, ch[N][2]; void input(LL x) { int u = 0; for (int i = 31; ~i; i -- ) { LL c = (x >> i) & 1; if (!ch[u][c]) ch[u][c] = ++ cnt; u = ch[u][c]; } } LL find_same(LL x) { LL ans = 0, u = 0; for (int i = 31; ~i; i -- ) { LL c = (x >> i) & 1, cc = c ^ 1; if (ch[u][c]) u = ch[u][c], ans <<= 1; else u = ch[u][cc], ans = ans << 1 | 1; } return ans; } LL find_another(LL x) { LL ans = 0, u = 0; for (int i = 31; ~i; i -- ) { LL c = (x >> i) & 1, cc = c ^ 1; if (ch[u][cc]) u = ch[u][cc], ans = ans << 1 | 1; else u = ch[u][c], ans <<= 1; } return ans; } } trie1, trie2; LL sum_pre[N], sum_last[N]; LL l[N], r[N]; int main() { int n; cin >> n; for (int i = 1; i <= n; i ++ ) { cin >> sum_pre[i]; sum_last[i] = sum_pre[i]; sum_pre[i] ^= sum_pre[i - 1]; l[i] = 1e18; } l[0] = 1e18; for (int i = n; i; i -- ) sum_last[i] ^= sum_last[i + 1]; for (int i = 1; i <= n; i ++ ) { trie1.input(sum_pre[i - 1]); l[i] = trie1.find_same(sum_pre[i]); l[i] = min(l[i], l[i - 1]); } for (int i = n; i >= 1; i -- ) { trie2.input(sum_last[i + 1]); r[i] = trie2.find_another(sum_last[i]); r[i] = max(r[i], r[i + 1]); } LL ans = -1e18; for (int i = 1; i < n; i ++ ) ans = max(ans, r[i + 1] - l[i]); cout << ans << endl; return 0; }
E:谁是天选之人
知识点:线段树
其实就是个线段树的板子题,当然这里还有另外一种解法, 用并查集维护
这里感谢SaladDays提供的思路
基本思路就是在这个序列中,每次划定的范围中,一个数是它右边的数的儿子。而我们填数由于以最后一次为准, 所以直接从后往前填即可,这里保证了每个点只遍历到一遍,所以复杂度是O(n)的,具体看代码。
#include <bits/stdc++.h> using namespace std; const int N = 1000100; int par[N], ans[N]; inline int find(int x) { if (x == par[x]) return x; return par[x] = find(par[x]); } int main() { int n, m, a, b; cin >> n >> m >> a >> b; for (int i = 1; i <= n; i ++ ) par[i] = i; for (int i = m; i; i -- ) { int l = (i * a + b) % n + 1, r = (i * b + a) % n + 1; if (l > r) swap(l, r); int root = find(l); while (root <= r) { par[root] = root + 1; ans[root] = i; root = find(root); } } for (int i = 1; i <= n; i ++ ) cout << ans[i] << endl; return 0; }