第一题:格雷码,构造,逆向思维
第二题:括号树,括号序列,栈,dfs,构造
第三题:树上的数,贪心,链表,构造
D1T1:格雷码
思路:第一眼看到格雷码感觉格外亲切,年纪大了,熬夜熬不动了,记忆力也衰退了不少,隐约记得格雷码曾经在某一次初赛的试题里出现过,当然也可能是模拟赛。包括SCOI 2015年还有一题《超级格雷码》,腾讯2016年笔试有一题是生成格雷码,LeetCode (一个面试海外IT公司很有用的题库)也有格雷码的题目,格雷码和二进制之间的转换应该是一个经典问题。
回归到本题当中,本题充分考察了选手逆向思维,如果80%的数据,采用构造格雷码的方法,那么根据k反推格雷码的规律也应该能够想出来。如果K的当前位为1,则格雷码该位也为1,然后把K剩下未处理的位取反,如果K的当前位为0,则对应的格雷码也为0,但不取反, 可以结合代码理解,建议动动笔推导一下。
#include
#include using namespace std; int main() { freopen("code.in","r",stdin); freopen("code.out","w",stdout); unsigned long long n, k; cin >> n >> k; for(long long i = n-1; i >= 0; --i) { if ((k >> i)&1) { cout << "1"; k = ~k; } else { cout << "0"; } } fclose(stdin); fclose(stdout); return 0; }
D1T2: 括号树
思路:括号序列和树上问题结合,点赞。其次特殊性质fi = i - 1 是一条链了,都是很好的得部分分的性质,链上合法括号序列很简单,所以这题没有50分以上的选手都值得反思。
括号序列如果你能想到stack然后计算对答案的贡献,其实还是可做的,首先建树从树根往下跑dfs,用栈来记录途中的括号,再考虑到一个位置对答案有贡献,能贡献的肯定是右括号,过程中记录待匹配的左括号即可,注意叠加即可,如果你想清楚链的问题,相信树的问题也能想通。所以本题仍旧是一个树的经典问题,如何在dfs过程中维护我们想要的数据。
注意结果开long long,在oitiku上试了一下不开longlong,只有50分。。。如果真的是longlong卡50分,还不如在题目里提示一下大家结果超int,RP++
#include
#include #include using namespace std; const int maxn = 500000 + 10; struct Edge { int v, next; } e[2 * maxn]; int n, fa[maxn], f[maxn], cnt[maxn], tot = 0; string s; stack stk; long long ans = 0, current = 0; void add(int u, int v) { tot += 1; e[tot].v = v; e[tot].next = f[u]; f[u] = tot; } void dfs(int x) { int cur = 0; if (!stk.empty() && s[stk.top() - 1] == '(' && s[x - 1] == ')') { cur = stk.top(); stk.pop(); cnt[x] = cnt[fa[cur]] + 1; } else { cnt[x] = 0; stk.push(x); } current += cnt[x]; ans = ans ^ (x * current); for (int i = f[x]; i > 0; i = e[i].next) { int v = e[i].v; dfs(v); } current -= cnt[x]; if (cur) stk.push(cur); else stk.pop(); } int main() { freopen("brackets.in","r",stdin); freopen("brackets.out","w",stdout); cin >> n; cin >> s; for (int i = 2; i <= n; ++i) { scanf("%d", &fa[i]); add(fa[i], i); } dfs(1); cout << ans << endl; fclose(stdin); fclose(stdout); return 0; }
D1T3:树上的数
题目大意是带权树,n-1条边,删边会调换连接这条边的两个点的权值,最后按点权排序,求结点编号的字典序最小的解。本题难度是有点大,但部分分给的也不少。
思路:考试时长3.5小时的情况下,第三题有链,有菊花图(n-1度的点是大菊花)拿个60分,岂不是DAY1 260还是美滋滋的。不少能力出色的选手由于无法驾驭自己的能力,导致最后还不如专心写部分分,这样的例子每年都数不胜数。链和菊花图本题给分数不低,且作为突破口思考是非常值得的。对于普通选数从构思到写和调试,一共3.5小时还是有点紧。
根据字典序最小,前面尽可能的小,大概能摸到一些贪心的思路。
本题出人已经在知乎上给出做法了,写的很详细:
https://www.zhihu.com/question/351347604/answer/896344216
#include
#include #include using namespace std; struct Edge { int u, v, next; } e[2005 * 2]; int n, p[2005]; int pre[2005][2005], nxt[2005][2005]; int cnt[2005], f[2005], ans[2005], tot = 0; void add(int u, int v) { tot += 1; e[tot].u = u; e[tot].v = v; e[tot].next = f[u]; f[u] = tot; } bool check(int l, int m, int r) { if (pre[m][r] == -1) return false; if (nxt[m][l] == -1) return false; if (nxt[m][l] == r) return false; if (nxt[m][l] == 0 && pre[m][r] == n + 1 && cnt[m] != 2) return false; return true; } void un(int l, int m, int r) { nxt[m][pre[m][r]] = nxt[m][l]; pre[m][nxt[m][l]] = pre[m][r]; pre[m][r] = nxt[m][l] = -1; cnt[m]--; } bool remove(int x, int y, int fa) { if (x == y) { un(fa, x, n + 1); return true; } for (int i = f[x]; i; i = e[i].next) { int v = e[i].v; if (v == fa) continue; if (remove(v, y, x)) { un(fa, x, v); return true; } } return false; } int find(int x, int fa) { int result = n; if (check(fa, x, n + 1) && x < n) result = x; for (int i = f[x]; i; i = e[i].next) { int v = e[i].v; if (v == fa) continue; if (check(fa, x, v)) result = min(result, find(v, x)); } return result; } void work() { cin >> n; tot = 0; for (int i = 0; i <= n; ++i) f[i] = 0; for (int i = 1; i <= n; ++i) scanf("%d", &p[i]); for (int i = 1; i <= n; ++i) { for (int j = 0; j <= n + 2; ++j) pre[i][j] = nxt[i][j] = j; } for (int i = 1; i <= n; ++i) cnt[i] = 2; for (int i = 1; i < n; ++i) { int u, v; scanf("%d%d", &u, &v); add(u, v); add(v, u); cnt[u] += 1; cnt[v] += 1; } for (int i = 1; i <= n; ++i) { int np = find(p[i], 0); ans[i] = np; remove(p[i], np, 0); } for (int i = 1; i < n; ++i) printf("%d ", ans[i]); printf("%d\n", ans[n]); } int main() { freopen("tree.in", "r", stdin); freopen("tree.out", "w", stdout); int T; cin >> T; while (T--) { work(); } return 0; fclose(stdin); fclose(stdout); }