20251229 - 月度检测总结
20251229 - 月度检测总结
考试情况:
Rank : 13
Score : 4
Penalty : 414
| 题目 | 是否AC | 罚时 | 补题情况 |
|---|---|---|---|
| A | Y | 0 | NULL |
| B | Y | 0 | NULL |
| C | N | NULL | Y |
| D | N | NULL | Y |
| E | N | NULL | Y |
| F | Y | -3 | NULL |
| G | N | NULL | N |
| H | Y | -3 | NULL |
| I | N | NULL | N |
A - Cut the Array
考试时发现样例不对,调了半天,但是发现之前的代码是对的!浪费了 10 min。
思路:暴力枚举 \(l\) 和 \(r\),再随便统计结果就好了。
void solve() {
n = read();
for (int i = 1; i <= n; i++) {
s[i] = 0;
a[i] = read();
s[i] = s[i - 1] + a[i];
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j < n; j++) {
int s1 = s[i] % 3;
int s2 = (s[j] - s[i]) % 3;
int s3 = (s[n] - s[j]) % 3;
s1 %= 3;
s2 %= 3;
s3 %= 3;
// printf("%d %d %d\n", s1, s2, s3);
if ((s1 != s2 && s2 != s3 && s1 != s3) || (s1 == s2 && s2 == s3)) {
printf("%d %d\n", i, j);
return;
}
}
}
// puts("???");
puts("0 0");
}
B - Maximum Cost Permutation
老师说要特判,但为什么我没有特判呢?
思路:想让排序的区间尽可能小,我们可以把较大的数字填到较小的空位里,这样就使得结果最大,最后的结果用两个指针扫一下即可。
set <int> st;
void solve() {
st.clear();
int n = read();
for (int i = 1; i <= n; i++) {
st.insert(-i);
a[i] = read();
}
for (int i = 1; i <= n; i++) {
if (a[i] != 0) {
st.erase(-a[i]);
}
}
for (int i = 1; i <= n; i++) {
if (a[i] == 0) {
a[i] = -(*st.begin());
st.erase(st.begin());
}
}
// for (int i = 1; i <= n; i++) {
// printf("%d ", a[i]);
// }
int l = 1, r = n;
while (l <= n && a[l] == l) {
l++;
}
while (r >= l && a[r] == r) {
r--;
}
printf("%d\n", r - l + 1);
}
C - Non-Descending Arrays
考试时想到了 dp,但是没写出来!
思路:可以设 \(dp_i,_j(j \in \{0, 1\})\) 为考虑前 \(i\) 个数,换或不换的好集合的数量。
转移一下就好了!
void solve() {
n = read();
for (int i = 1; i <= n; i++) a[i] = read(), dp[i][0] = dp[i][1] = 0;
for (int i = 1; i <= n; i++) b[i] = read(), dp[i][0] = dp[i][1] = 0;
dp[1][0] = dp[1][1] = 1;
for (int i = 2; i <= n; i++) {
if (a[i] >= a[i - 1] && b[i] >= b[i - 1]) dp[i][0] = (dp[i][0] + dp[i - 1][0]) % P;
if (a[i] >= b[i - 1] && b[i] >= a[i - 1]) dp[i][0] = (dp[i][0] + dp[i - 1][1]) % P;
if (b[i] >= a[i - 1] && a[i] >= b[i - 1]) dp[i][1] = (dp[i][1] + dp[i - 1][0]) % P;
if (b[i] >= b[i - 1] && a[i] >= a[i - 1]) dp[i][1] = (dp[i][1] + dp[i - 1][1]) % P;
}
printf("%lld\n", (dp[n][0] + dp[n][1]) % P);
}
D - 爆破
考试时一直在想 DSU,考完发现是一个 MST。如果在 m 条边选 \(n - 1\) 条边,就要想到 MST!!!
和 NOIP 2017 提高组] 奶酪 很像,把距离小于等于 \(h_i + h_j\) 的下标合并。
然后把不同于同一集合的集合建图,跑 Prim 或 Kruskal 即可。
int n, fa[N];
struct Node {
db x, y, r;
} a[N];
struct TreeNode {
int x, y;
db w;
bool operator < (const TreeNode &A) const {
return w < A.w;
}
};
vector <TreeNode> edges;
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void unite(int x, int y) {
x = find(x), y = find(y);
if (x != y) fa[x] = y;
}
db dist(int i, int j) {
db x = (a[i].x - a[j].x) * (a[i].x - a[j].x);
db y = (a[i].y - a[j].y) * (a[i].y - a[j].y);
return sqrt(x + y);
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= n; i++) {
scanf("%lf%lf%lf", &a[i].x, &a[i].y, &a[i].r);
for (int j = 1; j < i; j++) {
if (dist(i, j) <= a[i].r + a[j].r) {
unite(i, j);
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if (find(i) != find(j)) {
db w = max(dist(i, j) - a[i].r - a[j].r, 0.0);
edges.pb({i, j, w});
}
}
}
// for (int i = 1; i <= n; i++) fa[i] = i;
sort(edges.begin(), edges.end());
db ans = 0;
for (auto [x, y, w] : edges) {
x = find(x), y = find(y);
if (x != y) {
fa[x] = y;
ans += w;
}
}
printf("%.2lf\n", ans);
}
E - 燃烧
这是一个记忆化搜索的题目。
如果 \(a_i > a_j\),就可以求出小于子树权值的数量,再加上 \(1\) 即可。
暴力做肯定 TLE,记忆化搜索即可。
int n, a[N];
ll dp[N];
vector <int> edges[N];
inline ll dfs(int u, int fa) {
if (dp[u] != -1) return dp[u];
dp[u] = 0;
ll cnt = 0;
for (auto v : edges[u]) {
if (v == fa) continue;
if (a[u] > a[v]) {
cnt += dfs(v, u);
}
}
return dp[u] = cnt + 1;
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), dp[i] = -1;
for(int i = 1; i < n; i++) {
int x, y;
scanf("%d%d", &x, &y);
edges[x].pb(y);
edges[y].pb(x);
}
ll ans = 0;
for (int i = 1; i <= n; i++) ans = max(ans, dfs(i, i));
printf("%lld\n", ans);
}
F - Beautiful Permutation
这是一个交互式问题。
交互题详见 Interactive Problems: Guide for Participants - Codeforces
就是先求出他们的差,再读入两种方式,如果差等于 \([1, n]\) 的差,就可以挪动左指针,否则挪动右指针。
void ask(int op, int j, int &a) {
cout << op << " " << 1 << " " << j << endl;
cout.flush();
fflush(stdout);
cin >> a;
}
void solve() {
// fflush(stdout);
cin >> n;
int p;
ask(2, n, p);
p = p - (n * (n + 1) / 2);
int l = 0, r = n + 1;
while (l + 1 < r) {
int mid = (l + r) / 2;
ask(1, mid, x);
ask(2, mid, y);
if (x == y)
l = mid;
else
r = mid;
}
cout << "! " << l + 1 << " " << l + p << endl;
cout.flush();
fflush(stdout);
cout.flush();
fflush(stdout);
cout.flush();
fflush(stdout);
cout.flush();
fflush(stdout);
cout.flush();
fflush(stdout);
}
G - Laserphones S
思路:分层图,01 bfs,Dijkstra。
我待会再补。
H - 受欢迎的牛 G
如果缩点后有多个出度,一定无解,否则输出强联通分量的节点数量。
证明:如果答案不在强联通分量中,就一定有一条返祖边可以到达,但是这样就会变成强联通分量,与答案不在强联通分量中矛盾。
void Tarjan(int u) {
dfn[u] = low[u] = ++idx;
stk[++top] = u;
ins[u] = 1;
for (auto v : edges[u]) {
if (!dfn[v]) {
Tarjan(v);
low[u] = min(low[u], low[v]);
}else if (ins[v]){
low[u] = min(low[u], dfn[v]);
}
}
if (low[u] == dfn[u]) {
scc++;
while (1) {
int v = stk[top--];
ins[v] = 0;
bel[v] = scc;
k[scc].pb(v);
if (v == u) break;
}
}
}
void solve() {
n = read(), m = read();
for (int i = 1; i <= m; i++) {
int x = read(), y = read();
edges[x].push_back(y);
// edges[y].push_back(x);
}
for (int i = 1; i <= n; i++) if(!dfn[i]) Tarjan(i);
vector <int> d(n + 1);
for (int i = 1; i <= n; i++) {
for (auto v : edges[i]) {
if (bel[i] != bel[v]) {
G[bel[i]].push_back(bel[v]);
d[bel[i]]++;
}
}
}
int ans = 0, cnt = 0;
for (int i = 1; i <= scc; i++) {
if (!d[i]) {
cnt++;
ans = k[i].size();
}
}
printf("%d\n", cnt == 1 ? ans : 0);
}
总结:要多做一些思维题!拓宽思路!

浙公网安备 33010602011771号