Codeforces Round 897 (Div. 2)

写在前面

比赛地址:https://codeforces.com/contest/1867

简略题解。

还好没掉分。

A

令原数列中第 k 大对应 k 即可。

复制复制
//
/*
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

考虑给定串前后的对应部分原来是否相同,若不同则需要且仅需要在这两个位置中改一个,若相同则可以都不改或者都改。特别地,若 n 为奇数则中间位置改不改都行。

于是分别记录两种位置的数量 c0,c1,显然 tc0+2×k(0kc1)=1。若原串位置为奇数则额外有 tc0+2×k+1=1

//
/*
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 的交互很亲民,望周知。

第一次加 mex,之后删什么加什么。

正确性显然。

//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

套路。

先特判 k=1,此时当且仅当 bi=i 有解;若 k1,则当 bi=i 时肯定无解。

手玩一下可以发现,若对于位置 {p1,p2,,pk} 满足 bp1=p2,bp2=p3,,bpk=p1,那么进行一次 l={p1,p2,,pk} 的操作即可将这些位置全部修改成目标。

发现上面的情况类似于构成了一个大小为 k 的环,转换为图论模型,从节点 iai 连边,则没有零出度的点,构成了一片基环内向森林。

显然,若其中的所有基环内向树都是大小为 k 的环构成时皆大欢喜,分别对环中的节点代表的位置进行一次操作即可;否则若所有基环内向树中仅有大小为 k 的环时,可以考虑按照拓扑序先对链上的节点代表的位置进行操作,最后再对环中的节点代表的位置进行一次操作即可。通过上面的操作方案可以推出若出现大小不为 k 的环时无解。

于是仅需 Tarjan 检查原图中的所有环大小是否为 k 即可。总时间复杂度 O(n) 级别。

//
/*
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 写了就飞升了。

钦定了 n,k 均为偶数,且 knk2,且有 k22500,显然进行不多于 k 次操作就可以覆盖整个数列。

kn 皆大欢喜,进行 nk 次操作恰好覆盖整个数列即可,否则考虑如何构造出最后的长度为 nmodk 的不完整段的异或和。

首先想到必须对 [nk+1,n] 进行一次操作来得到这段的异或和,然后考虑通过异或去除重复部分的贡献。由于有进行一次操作后被操作的序列翻转的性质,一个显然的想法是在对 [nk+1,n] 进行操作前先对它之前的某一段进行操作,在获得了一部分信息的同时,将重复部分翻转到 [nk+1,n] 中,再对它进行操作来获得全部的信息。

s=k×nk+1,即最后不完整段的第一个位置,钦定了 n,k 为偶数,手玩一下可以发现先对 [n+s2k+1,n+s2] 进行一次操作,再对 [nk+1,n] 进行一次操作,两次操作的结果的异或即为最后不完整段的异或和。

上述过程仅需 k+252 次操作,稳过。

//
/*
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 的交互题很亲密,望周知。
posted @   Luckyblock  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示