Codeforces Round 897 (Div. 2)
写在前面
比赛地址:https://codeforces.com/contest/1867。
简略题解。
还好没掉分。
A
令原数列中第 大对应 即可。
复制复制// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 4e4 + 10; //============================================================= int n, num, b[kN]; struct Data { int a, pos; } a[kN]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } bool cmp(Data fir_, Data sec_) { return fir_.a > sec_.a; } //============================================================= int main() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { n = read(); for (int i = 1;i <= n; ++ i) a[i] = (Data) {read(), i}; std::sort(a + 1, a + n + 1, cmp); for (int i = 1; i <= n; ++ i) b[a[i].pos] = i; for (int i = 1; i <= n; ++ i) printf("%d ", b[i]); printf("\n"); } return 0; }
B
考虑给定串前后的对应部分原来是否相同,若不同则需要且仅需要在这两个位置中改一个,若相同则可以都不改或者都改。特别地,若 为奇数则中间位置改不改都行。
于是分别记录两种位置的数量 ,显然 。若原串位置为奇数则额外有 。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 1e5 + 10; //============================================================= int n, flag1, cnt0, cnt1; char s[kN]; bool ans[kN]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } //============================================================= int main() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { n = read(); scanf("%s", s + 1); flag1 = n % 2, cnt0 = cnt1 = 0; for (int i = 1, j = n; i < j; ++ i, -- j) { if (s[i] == s[j]) ++ cnt0; else ++ cnt1; } for (int i = 0; i <= n; ++ i) ans[i] = 0; if (flag1) { for (int i = cnt1, j = 0; j <= cnt0; i += 2, ++ j) ans[i] = ans[i + 1] = 1; } else { for (int i = cnt1, j = 0; j <= cnt0; i += 2, ++ j) ans[i] = 1; } for (int i = 0; i <= n; ++ i) printf("%d", ans[i] ? 1 : 0); printf("\n"); } return 0; } /* 101 011 1001 0 0011 2 2 1 0 0 1 */
C
傻逼交互、、、看到交互就先跑了亏死。
CF 的交互很亲民,望周知。
第一次加 ,之后删什么加什么。
正确性显然。
//By:Luckyblock /* */ #include <cstdio> #include <cctype> #include <algorithm> const int kN = 1e5 + 10; //============================================================= int n, mex, a[kN]; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = - 1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + ch - '0'; return f * w; } //============================================================= int main() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { n = read(); mex = 0; for (int i = 1; i <= n; ++ i) { a[i] = read(); if (a[i] == mex) ++ mex; } for (int cnt = 1, del; ; ++ cnt) { if (cnt % 2 == 1) { if (cnt == 1) { printf("%d\n", mex); fflush(stdout); ++ mex; for (int i = mex; i <= n; ++ i) if (a[i] == mex) ++ mex; } else { printf("%d\n", del); fflush(stdout); } } else { del = read(); if (del == -1) break; if (del == -2) return 0; } } } return 0; }
D
套路。
先特判 ,此时当且仅当 有解;若 ,则当 时肯定无解。
手玩一下可以发现,若对于位置 满足 ,那么进行一次 的操作即可将这些位置全部修改成目标。
发现上面的情况类似于构成了一个大小为 的环,转换为图论模型,从节点 向 连边,则没有零出度的点,构成了一片基环内向森林。
显然,若其中的所有基环内向树都是大小为 的环构成时皆大欢喜,分别对环中的节点代表的位置进行一次操作即可;否则若所有基环内向树中仅有大小为 的环时,可以考虑按照拓扑序先对链上的节点代表的位置进行操作,最后再对环中的节点代表的位置进行一次操作即可。通过上面的操作方案可以推出若出现大小不为 的环时无解。
于是仅需 Tarjan 检查原图中的所有环大小是否为 即可。总时间复杂度 级别。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long const int kN = 1e6 + 10; //============================================================= int n, m, k, a[kN]; int edgenum, head[kN], v[kN << 1], ne[kN << 1]; int dfnnum, belnum, dfn[kN], low[kN], bel[kN], sz[kN]; std::stack <int> st; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } void Add(int u_, int v_) { v[++ edgenum] = v_; ne[edgenum] = head[u_]; head[u_] = edgenum; } void Tarjan(int u_) { low[u_] = dfn[u_] = ++ dfnnum; st.push(u_); for (int i = head[u_]; i; i = ne[i]) { int v_ = v[i]; if (!dfn[v_]) { Tarjan(v_); low[u_] = std::min(low[u_], low[v_]); } else if (!bel[v_]) { low[u_] = std::min(low[u_], dfn[v_]); } } if (low[u_] == dfn[u_]) { ++ belnum; for (int now = 0; now != u_; st.pop()) { now = st.top(); bel[now] = belnum; ++ sz[belnum]; } } } void Init() { n = read(), k = read(); edgenum = dfnnum = belnum = 0; while (!st.empty()) st.pop(); for (int i = 1; i <= n; ++ i) { head[i] = dfn[i] = low[i] = bel[i] = sz[i] = 0; } for (int i = 1; i <= n; ++ i) a[i] = read(), Add(i, a[i]); } //============================================================= int main() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { Init(); int flag = 1; if (k == 1) { for (int i = 1; i <= n; ++ i) if (a[i] != i) flag = 0; printf("%s\n", flag ? "YES" : "NO"); continue; } else { for (int i = 1; i <= n; ++ i) if (a[i] == i) flag = 0; if (!flag) { printf("NO\n"); continue; } } for (int i = 1; i <= n; ++ i) if (!dfn[i]) Tarjan(i); for (int i = 1; i <= belnum; ++ i) { if (sz[i] > 1 && sz[i] != k) { flag = 0; break; } } printf("%s\n", flag ? "YES" : "NO"); } return 0; }
E1/E2
傻逼交互、、、
写完 D 就下班了亏死,要是把 E 写了就飞升了。
钦定了 均为偶数,且 ,且有 ,显然进行不多于 次操作就可以覆盖整个数列。
若 皆大欢喜,进行 次操作恰好覆盖整个数列即可,否则考虑如何构造出最后的长度为 的不完整段的异或和。
首先想到必须对 进行一次操作来得到这段的异或和,然后考虑通过异或去除重复部分的贡献。由于有进行一次操作后被操作的序列翻转的性质,一个显然的想法是在对 进行操作前先对它之前的某一段进行操作,在获得了一部分信息的同时,将重复部分翻转到 中,再对它进行操作来获得全部的信息。
设 ,即最后不完整段的第一个位置,钦定了 为偶数,手玩一下可以发现先对 进行一次操作,再对 进行一次操作,两次操作的结果的异或即为最后不完整段的异或和。
上述过程仅需 次操作,稳过。
// /* By:Luckyblock */ #include <bits/stdc++.h> #define LL long long //============================================================= int n, k; //============================================================= inline int read() { int f = 1, w = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1; for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); return f * w; } int Query(int x_) { printf("? %d\n", x_); fflush(stdout); return read(); } //============================================================= int main() { // freopen("1.txt", "r", stdin); int T = read(); while (T --) { n = read(), k = read(); int i = 1, ans = 0; for (i = 1; i + k - 1 <= n; i += k) { ans ^= Query(i); } if (i <= n) { ans ^= Query((i + n) / 2 - k + 1); ans ^= Query(n - k + 1); } printf("! %d\n", ans); fflush(stdout); } return 0; }
F
咕
写在最后
- 如果有轮换的性质可以考虑套路地建立图论模型。
- CF 的交互题很亲密,望周知。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】