CF1784F 不需要注意力的做法
liangbowen · · 题解
讲课题,大概是丢了三个条件,然后说这个就是充要条件。这个堆数值也太恶心了吧!所以就自己想了一下,得到了如下奶龙做法。我觉得这个做法是最容易的啊,怎么没人这样做呢。
编充要条件
考虑改成对操作序列计数,为了不重不漏,要对每个本质不同的集合选出一个代表元,然后对代表元计数。于是考虑如下打表程序(其中 A 是操作 B 是操作
int n, k; vector <int> A; map <vector <int>, vector <int>> mp;
void erase(int x) {
vector <int> T;
for (int i = 0; i < x; i++) T.push_back(A[i]);
for (int i = x + 2; i < A.size(); i++) T.push_back(A[i]);
A = T;
}
int main() {
scanf("%d%d", &n, &k);
for (int S = 0; S < (1 << k); S++) {
A.clear(); for (int i = 1; i <= 2 * n; i++) A.push_back(i);
for (int i = 0; i < k; i++) if (S >> i & 1) erase(0); else erase(A.size() / 2 - 1);
mp[A].push_back(S);
}
printf("%d\n", (int)mp.size());
for (auto [sta, vec] : mp) {
for (int i : sta) printf("%d ", i); puts("");
for (int S : vec) {for (int i = 0; i < k; i++) putchar((S >> i & 1) ? 'A' : 'B'); puts("");}
}
return 0;
}
输入 10 5,截取某一项:
5 6 7 8 9 16 17 18 19 20
AABBB
ABABB
BAABB
ABBAB
BABAB
很难不发现 AB 的数量不变。回到原题,手玩发现 ABB 与 BAB 是等价的,我们考虑将所有 ABB 变成 BAB,就得到了一个代表元,例如上面例子的代表元即为 BABAB。
看起来直接做完了,然而换个输入就爆了?发现只有
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#define mems(x, v) memset(x, v, sizeof x)
#define mcpy(x, y) memcpy(x, y, sizeof x)
using namespace std;
typedef pair <int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef long double wisdom;
int n, k; vector <int> A; map <vector <int>, vector <int>> mp;
void erase(int x) {
vector <int> T;
for (int i = 0; i < x; i++) T.push_back(A[i]);
for (int i = x + 2; i < A.size(); i++) T.push_back(A[i]);
A = T;
}
bool valid(int S) {
for (int i = 0; i + 2 < k; i++) if ((S >> i & 1) && !(S >> (i + 1) & 1) && !(S >> (i + 2) & 1)) return false;
return true;
}
int main() {
scanf("%d%d", &n, &k);
for (int S = 0; S < (1 << k); S++) {
A.clear(); for (int i = 1; i <= 2 * n; i++) A.push_back(i);
for (int i = 0; i < k; i++) if (S >> i & 1) erase(0); else erase(A.size() / 2 - 1);
mp[A].push_back(S);
}
printf("%d\n", (int)mp.size());
for (auto [sta, vec] : mp) {
int tot = 0; for (int S : vec) if (valid(S)) tot++;
if (tot >= 2) {
printf("[%d] ",(int)vec.size()); for (int i : sta) printf("%d ", i); puts("");
for (int S : vec) {for (int i = 0; i < k; i++) putchar((S >> i & 1) ? 'A' : 'B'); puts("");}
}
}
return 0;
}
其中一个:
[8] 15 16 17 20 21 22
BBBAAAAB
ABBAAAAB
BABAAAAB
AABAAAAB
BBAAAAAB
ABAAAAAB
BAAAAAAB
AAAAAAAB
随便输点数,只要你是正常人都能注意到:出现错误,是因为前半段删除过程中 AB 打架了,串的一个前缀可以任意选择。那直接钦定前缀这一坨全由 A 来删除即可。要钦定多长?设长度为
也就是,设序列最小值为
然后还要保证
先删掉 ABB,且不会删到 A 的总数。对于第二个条件,发现实质是在间隔之间维护了一个指针,初始指在 A 所以初始指在 A 就 B 就
Fun Fact:不是啊,我草,我题解都写完了,我还以为自己科研了一个新做法呢,结果 TM 的官方题解就是这个做法???那为啥洛谷题解没有啊???那就这样吧,诶。