博客 > 数据结构&算法 > 题目&比赛
# Codeforces Round 928 (Div. 4) <https://codeforces.com/contest/1926> # ABCD太简单略了 # E 首先,**只有奇数本身和其2、4、8、16……倍存在首先被遍历的可能性**,即首先遍历全部奇数,然后是奇数的2倍,然后是4倍,然后是8倍……这个容易推出,不做证明。 可以用筛法得到奇数本身、*2、*4、*8……等序列(可分别称为1序列、2序列、4序列、8序列……),但是仅能用于研究,打表处理不了1e9的规模,结果如: ``` 2: 1 / 2 4: 1 3 / 2 / 4 9: 1 3 5 7 9 / 2 6 / 4 / 8 19: 1 3 5 7 9 11 13 15 17 19 / 2 6 10 14 18 / 4 12 / 8 / 16 20: 1 3 5 7 9 11 13 15 17 19 / 2 6 10 14 18 / 4 12 20 / 8 / 16 ``` 这个序列有分形的特性: - 4的2及之后序列同构于2的所有序列 - 9的2及之后序列同构于4的所有序列 - 9的4及之后序列同构于2的所有序列 - 19的2及之后序列同构于9的所有序列 - …… 因此我们可以找到 $\text O(\log n)$ 的**规律**: - 1序列长度 $l_1 = \lceil n/2\rceil$ - 2序列长度 $l_2 = \lceil (n-l_1)/2\rceil$ - 4序列长度 $l_4 = \lceil (n-l_1-l_2)/2\rceil$ - $2^m$序列长度 $l_{2^m} = \lceil (n-\sum_{i=0}^{m-1}l_{2^i})/2\rceil$ - …… 假设查询位置为$k$,那么有如下结论: - ①从$k$中依次减去可全部经过的序列的长度,只剩下减不动的长度$k'$。比如 9 的$l_1=5$,$l_2=2$,假设$k=7$,则$k'=7-5=2$,意味着查询 $k$ 落在了 9 的$2$序列的第$2$个元素上 - ②因为分形的规律,$2^m$序列的第$k'$个元素,一定是正奇数序列的第$k'$个元素的$2^m$倍,具体数字即为 $2^m\cdot(2k'-1)$,其中$k'\ge1$ ```cpp int main() { //ios_base::sync_with_stdio(false); int qwq; INI(qwq); F0(t, qwq) { int n, m; INI(n); INI(m); int p = 1; F0(i, 32) { int len = n - n / 2; if (m <= len) { printf("%d\n", p * (2 * m - 1)); break; } p *= 2; m -= len; n -= len; } } return 0; } ``` ------ # F 首先找到存在冲突的黑块(中心块)以及可以尝试删除的块(中心块及其四角块)。考虑枚举+回溯法,删除某些块,判断是否解开局面,返回解的代价,并筛选最优解。 另外还有一个特性:在此棋盘上,奇格(x+y为奇数)和偶格(x+y为偶数)是互不干扰的,奇格的冲突只能由奇格解开,偶格的冲突只能由偶格解开。这个特性可以作为剪枝的依据,分别在奇偶两个图层上找解,然后将代价相加即可。 本题DFS相当难写(虽然原理不变),在此放一份tourist使用位压缩+枚举的代码: ```cpp /** * author: tourist * created: 19.02.2024 09:34:20 **/ #include <bits/stdc++.h> using namespace std; #ifdef LOCAL #include "algo/debug.h" #else #define debug(...) 42 #endif int main() { ios::sync_with_stdio(false); cin.tie(0); int tt; cin >> tt; while (tt--) { int n = 7; vector<string> s(n); for (int i = 0; i < n; i++) { cin >> s[i]; } int ans = 0; for (int p = 0; p < 2; p++) { // 处理奇偶图层 // 构建冲突格集bad int64_t bad = 0; for (int i = 1; i < n - 1; i++) { for (int j = 1; j < n - 1; j++) { if ((i + j) % 2 != p) { // 处理奇偶图层 continue; } if (s[i][j] == 'B' && s[i - 1][j - 1] == 'B' && s[i - 1][j + 1] == 'B' && s[i + 1][j - 1] == 'B' && s[i + 1][j + 1] == 'B') { bad |= int64_t(1) << (i * n + j); } } } // 构建棋盘每一格(i,j)的解开格集kill[i][j] vector<vector<int64_t>> kill(n, vector<int64_t>(n)); for (int i = 1; i < n - 1; i++) { for (int j = 1; j < n - 1; j++) { kill[i][j] |= int64_t(1) << (i * n + j); kill[i][j] |= int64_t(1) << ((i - 1) * n + (j - 1)); kill[i][j] |= int64_t(1) << ((i - 1) * n + (j + 1)); kill[i][j] |= int64_t(1) << ((i + 1) * n + (j - 1)); kill[i][j] |= int64_t(1) << ((i + 1) * n + (j + 1)); } } int best = n * n + 1; // 7*7的棋盘上,值得删除的格必然在5*5范围内,而其中单论奇格或偶格的话最多13个 // 因此将删除格集t从0遍历到2^14,枚举所有的删除情况,即可找到解 for (int t = 0; t < (1 << 13); t++) { int at = 0; int64_t done = 0; for (int i = 1; i < n - 1; i++) { for (int j = 1; j < n - 1; j++) { if ((i + j) % 2 != p) { // 处理奇偶图层 continue; } if ((t >> at) & 1) { // 判断第at个奇格(偶格)是否包含在删除格集t中? done |= kill[i][j]; // done代表本轮枚举的删除格集t可以解开的所有位置 } at += 1; } } // 若本轮枚举的解开位置done包含了原局面的冲突格集bad,则是一个有效解 if ((done & bad) == bad) { best = min(best, __builtin_popcount(t)); } } ans += best; // 在奇格和偶格中分别有一个best,将这两个加到ans里 } cout << ans << '\n'; } return 0; } ``` > 奇偶特性: > > ![c4fc9e9f9c659fec85fd5637bb334355.png](/resources/b8459c9f94ab4464941fdc6d865cc3c3) ------ # G 首先可以知道,当派对音乐完成传播后,整棵树上的每个节点要么是安静的,要么是扰民的,即睡眠节点必为安静态,派对节点必为扰民态,中立节点将根据是否被传播音乐而呈现为扰民态或安静态。 由于是树,比图具有更好的性质,因此我们直接尝试从叶子节点开始,自底向上判断每个节点的情况: - 对睡眠节点 - 其一定向上传递为安静态 - 断开其与所有扰民态子节点的边 - 对派对节点 - 其一定向上传递为扰民态 - 断开其与所有安静态子节点的边 - 对中立节点 - 忽略中立态子节点 - 若安静态和睡眠态子节点个数不同,则向上传递占优状态。断开与非占优状态的子节点的边。*验证可知断开非占优状态的边一定不比断开占优状态的边差*。 - 若安静态和睡眠态子节点个数相同,则向上传递中立态。断开与任一状态子节点的边。*验证可知选择哪一个都不影响传播全部完成后最终断开的边的条数*。 - 无子节点和一个子节点的情况都可以归约到此通用方法 - 上文所说的“验证”都是指:判断当前节点的父节点分别是扰民和安静时,与父节点的边是否需要断开,以及最终断开的边的条数是否最优。 综合来看,这个算法可以被归为贪心法。除此之外还有人用DP做,基本思路也是这样枚举各类情况,但让搜索空间更大。 ```cpp // -------------- HEAD ---------------- #include <bits/stdc++.h> using namespace std; typedef long long LL; typedef unsigned long long ULL; const double PI = acos(-1.0); const int INF = 0x3f3f3f3f; #define _CRT_SECURE_NO_WARNINGS //#undef DEBUG #ifdef DEBUG #define ___LOG_PREFIX ">>> LOG: " #define LOG(x) {std::cout << ___LOG_PREFIX << x << endl;} #define LOGF printf(___LOG_PREFIX);printf #else #define LOG (void) #define LOGF (void) #endif #define INI(x) scanf("%d", &(x)) #define IND(x) scanf("%lf", &(x)) #define INLL(x) scanf("%lld", &(x)) #define INS_BUF(x,buf) \ { \ char ___##x[buf]; \ scanf("%s", ___##x); \ x = ___##x; \ } #define INS(x) INS_BUF(x, 10000) #define F0(i,n) for(int i=0;i<n;++i) #define F1(i,n) for(int i=1;i<=n;++i) #define F0R(i,n) for(int i=n-1;i>=0;--i) #define F1R(i,n) for(int i=n;i>0;--i) #define FE(i,c) for(auto &i:c) #define fill0(c) memset(c,0,sizeof(c)) #define mp make_pair #define mt make_tuple #define endl "\n" // -------------- GLOBAL ---------------- struct TreeNode { vector<int>* c = NULL; int p = 0; char ch = 0; int status = 0; // -1: 安静 0: 中立 1: 扰民 }; TreeNode tree[200000]; // -------------- FUNC ---------------- void addChild(int p, int c) { tree[c].p = p; if (tree[p].c == NULL) tree[p].c = new vector<int>(); tree[p].c->push_back(c); } void init(int n) { F0(i, 200000) { if (tree[i].c != NULL) delete tree[i].c; } fill0(tree); } int postOrder(int node) { if (tree[node].c == NULL) { return 0; } int removed = 0; FE(c, *tree[node].c) { removed += postOrder(c); } if (tree[node].status != 0) { FE(c, *tree[node].c) { if (tree[c].status == -tree[node].status) removed++; } } else { int s = 0, p = 0; FE(c, *tree[node].c) { if (tree[c].status == -1) s++; else if (tree[c].status == 1) p++; } if (s > p) { removed += p; tree[node].status = -1; } else if (s < p) { removed += s; tree[node].status = 1; } else { removed += s; tree[node].status = 0; } } return removed; } // -------------- MAIN ---------------- int main() { //ios_base::sync_with_stdio(false); int qwq; INI(qwq); F0(t, qwq) { int n; INI(n); init(n); F1(i, n - 1) { int t; INI(t); addChild(t, i + 1); } getchar(); F1(i, n) { tree[i].ch = getchar(); if (tree[i].ch == 'P') tree[i].status = 1; else if (tree[i].ch == 'S') tree[i].status = -1; } printf("%d\n", postOrder(1)); } return 0; } ```