A - 123233
模拟即可。
点击查看代码
void solve() {
int cnt[10]{};
int n;
std::cin >> n;
while (n) {
++ cnt[n % 10];
n /= 10;
}
for (int i = 1; i <= 3; ++ i) {
if (cnt[i] != i) {
std::cout << "No\n";
return;
}
}
std::cout << "Yes\n";
}
B - Hurdle Parsing
题意:给你一个字符串,用'|',分割了一些'-',问每一段'-'的个数是多少。
模拟。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
for (int i = 0; i + 1< s.size(); ++ i) {
int j = i + 1;
while (j < s.size() && s[j] == '-') {
++ j;
}
std::cout << j - i - 1 << " ";
i = j - 1;
}
std::cout << "\n";
}
C - Move Segment
题意:给你一个\(01\)串,把第\(k\)个\(1\)的连通块移到前面\(1\)的后面。
模拟。
点击查看代码
void solve() {
int n, k;
std::cin >> n >> k;
std::string s;
std::cin >> s;
int cnt = s[0] == '1', last = -1;
for (int i = 0; i < n; ++ i) {
if (s[i] == '0' && s[i + 1] == '1') {
++ cnt;
if (cnt == k) {
int j = i + 1;
++ last;
while (j < n && s[j] == '1') {
std::swap(s[j], s[last]);
++ j, ++ last;
}
break;
}
}
if (s[i] == '1') {
last = i;
}
}
std::cout << s << "\n";
}
D - Strange Mirroring
题意:对于一个字符串,每次把它大小写改变然后接到后面,重复\(10^{100}\)次,\(q\)次询问,每次问第\(k\)个字符。
模拟单个字符发现,每次长度是成倍数增长,第\(n + 2^in\)个地方一定是取反的,因为这个地方就是第一个字符变过来的,这提示我们找到\(k\)在的块,然后就变成了一个子问题,看要找多少次记录取反次数就行。
点击查看代码
void solve() {
std::string s;
std::cin >> s;
int n = s.size();
int q;
std::cin >> q;
while (q -- ) {
i64 x;
std::cin >> x;
int cnt = 0;
while (x > n) {
i64 k = 1;
while ((__int128)k * n < x) {
k *= 2;
}
x -= (__int128)k * n / 2;
++ cnt;
}
char c = s[x - 1];
if (cnt & 1) {
c = c >= 'a' ? c - 32 : c + 32;
}
std::cout << c << " \n"[!q];
}
}
E - 1D Bucket Tool
题意:\(n\)个格子每个格子的颜色为\(i\),有两种操作:
- 让\(x\)和所有与他相邻并且颜色相同的格子颜色都变成\(y\)。
- 询问颜色为\(x\)的有多少个格子。
容易想到用并查集维护颜色相同的块,那么当改变一个格子颜色时,判断是不是要和这个块最左边位置减一的位置是不是同一个颜色,和这个块最右边位置加一的位置是不是同一个颜色。那么我们需要用并查集记录一个集合的颜色,个数,最左边位置和最右边位置。
点击查看代码
struct DSU {
std::vector<int> fa, cnt;
DSU(int _n) {
fa.assign(_n + 1, 0);
cnt.assign(_n + 1, 1);
std::iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return;
}
fa[y] = x;
cnt[x] += cnt[y];
cnt[y] = 0;
}
int same(int x, int y) {
return find(x) == find(y);
}
int size(int x) {
return cnt[find(x)];
}
};
void solve() {
int n, q;
std::cin >> n >> q;
DSU ds(n);
std::vector<int> id(n + 1), min(n + 1), max(n + 1), cnt(n + 1, 1);
std::iota(id.begin(), id.end(), 0);
std::iota(min.begin(), min.end(), 0);
std::iota(max.begin(), max.end(), 0);
while (q -- ) {
int op;
std::cin >> op;
if (op == 1) {
int x, y;
std::cin >> x >> y;
if (id[ds.find(x)] != y) {
cnt[y] += ds.size(x);
cnt[id[ds.find(x)]] -= ds.size(x);
id[ds.find(x)] = y;
int l = min[ds.find(x)] - 1, r = max[ds.find(x)] + 1;
if (l && id[ds.find(l)] == y) {
min[ds.find(x)] = min[ds.find(l)];
ds.merge(x, l);
}
if (r <= n && id[ds.find(r)] == y) {
max[ds.find(x)] = max[ds.find(r)];
ds.merge(x, r);
}
}
} else {
int x;
std::cin >> x;
std::cout << cnt[x] << "\n";
}
}
}
F - Exchange Game
题意:有两个人,第一个人有\(n\)张牌,第二个人有\(m\)张牌,桌子上还有\(k\)张牌。两个人轮流出牌,如果桌子上有一张牌小于当前这个人出的牌,那么当前这个人可以拿走这张牌。问第一个人能不能赢。
注意到\(n+m+k<=12\),想到爆搜,定义\(dfs(u, x, y), (u \in \{0, 1\})\)为出牌人为\(u\)第一个拥有牌的状态为\(x\),第二个为\(y\),这里的\(x,y\)都是二进制数,利用状压表示有没有某一张牌。那么每次枚举当前这个人出哪张牌和是否拿走某张牌。和\(sg\)函数一样,不能走为必败态,如果当前状态能走到一个必败态则是必胜态,否则时必败态。
点击查看代码
const int N = 1 << 12;
int f[2][N][N];
int a[12];
int n, m, k;
int dfs(int u, int x, int y) {
if (f[u][x][y] != -1) {
return f[u][x][y];
}
int c = ~(x | y);
int res = 0;
if (u == 0) {
for (int i = 0; i < n + m + k; ++ i) {
if (x >> i & 1) {
res |= !dfs(u ^ 1, x - (1 << i), y);
for (int j = 0; j < n + m + k; ++ j) {
if ((c >> j & 1) && a[j] < a[i]) {
res |= !dfs(u ^ 1, x - (1 << i) + (1 << j), y);
}
}
}
}
} else {
for (int i = 0; i < n + m + k; ++ i) {
if (y >> i & 1) {
res |= !dfs(u ^ 1, x, y - (1 << i));
for (int j = 0; j < n + m + k; ++ j) {
if ((c >> j & 1) && a[j] < a[i]) {
res |= !dfs(u ^ 1, x, y - (1 << i) + (1 << j));
}
}
}
}
}
f[u][x][y] = res;
return res;
}