Codeforces Round #739 (Div. 3)
A. Dislike of Threes
找到第 k k k个既不是 3 3 3的倍数,个位数上也不是 3 3 3的数,也已预处理然后 O ( 1 ) O(1) O(1)输出,也可直接 f o r for for循环暴力。
#include <bits/stdc++.h>
using namespace std;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
vector<int> a;
for (int i = 1; i <= 2000; i++) {
if (i % 3 == 0 || i % 10 == 3) {
continue;
}
a.push_back(i);
}
int T, n;
cin >> T;
while (T--) {
cin >> n;
cout << a[n - 1] << "\n";
}
return 0;
}
B. Who’s Opposite?
由 2 × n 2 \times n 2×n个数按照顺序构成一圈, i i i的对立是 i + n i + n i+n,给定两个对立的 a , b a, b a,b,求 c c c的对立是谁。
容易发现 a b s ( a − b ) = n abs(a - b) = n abs(a−b)=n,所以只要判断 a , b , c a, b, c a,b,c是否合法 ≤ 2 × n \le 2 \times n ≤2×n,然后判断 c c c在前半圈还是后半圈即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T;
cin >> T;
while (T--) {
int a, b, c;
cin >> a >> b >> c;
if (a > b) {
swap(a, b);
}
int haf = b - a;
if (c > haf * 2 || a > haf * 2 || b > haf * 2) {
puts("-1");
}
else {
if (c > haf) {
printf("%d\n", c - haf);
}
else {
printf("%d\n", c + haf);
}
}
}
return 0;
}
C. Infinity Table
可以得到,第 i i i次书写,有 2 × i − 1 2 \times i - 1 2×i−1个数字,所以直接模拟即可,复杂度 T k T \sqrt k Tk,当然也可以预处理一下,然后二分 O ( k + T log k ) O(\sqrt k + T \log k) O(k+Tlogk)。
#include <bits/stdc++.h>
using namespace std;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; ; i++) {
int cur = 2 * i - 1;
if (n > cur) {
n -= cur;
}
else {
if (n <= i) {
printf("%d %d\n", n, i);
}
else {
n -= i;
printf("%d %d\n", i, i - n);
}
break;
}
}
}
return 0;
}
D. Make a Power of Two
给定一个数字字符串,可以移走其中任意数字,或者在末尾添加任意数字,要求在最少的步骤将其变为 2 2 2的幂次。
考虑得到 2 60 2 ^{60} 260以内所有 2 2 2的幂次数字的字符串,让给定字符串在上面按照顺序匹配,
找到能匹配上的一个最大子序列,然后计算操作次数,不断取最小值即可,整体复杂度 O ( 60 × 10 × T ) O(60 \times 10 \times T) O(60×10×T)。
#include <bits/stdc++.h>
using namespace std;
string a[60];
void init() {
for (int i = 0; i < 60; i++) {
long long cur = 1ll << i;
while (cur) {
a[i] += char(cur % 10 + '0');
cur /= 10;
}
reverse(a[i].begin(), a[i].end());
}
}
int f(string str) {
int ans = 0x3f3f3f3f;
for (int i = 0; i < 60; i++) {
int sum = 0, p1 = 0, p2 = 0, n = str.size(), m = a[i].size();
while (p1 < n && p2 < m) {
if (str[p1] == a[i][p2]) {
p1++, p2++, sum++;
}
else {
p1++;
}
}
ans = min(ans, n - sum + m - p2);
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
init();
int T;
cin >> T;
while (T--) {
string str;
cin >> str;
cout << min(f(str), (int)str.size() + 1) << "\n";
}
return 0;
}
E. Polycarp and String Transformation
考虑对串从后往前开始做,不难得到删除字母的顺序,我们再对字母的个数统计一下,加入字母 c c c是在第 k k k次删除,总共出现了 x x x次,
那么我们可以得出字母 c c c在原串中出现的次数就是 x k \frac{x}{k} kx次,通过这个,我们可以统计出原串的长度,
然后再对原串 O ( 不 同 字 母 个 数 × l e n t h ) O(不同字母个数 \times lenth) O(不同字母个数×lenth),统计模拟一下即可得到一个新的串,然后与给定串对比一下是否一样即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int num[30], vis[N], len, cnt, n;
char str[N];
bool judge(int len) {
string s;
for (int i = 1; i <= len; i++) {
s += str[i];
}
string ans = s;
for (int i = 1; i <= cnt; i++) {
string cur = "";
for (auto it : s) {
if (vis[i] == it - 'a') {
continue;
}
cur += it;
}
ans += cur;
s = cur;
}
if (n != ans.size()) {
return false;
}
for (int i = 1; i <= n; i++) {
if (str[i] != ans[i - 1]) {
return false;
}
}
return true;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int T;
scanf("%d", &T);
while (T--) {
scanf("%s", str + 1);
n = strlen(str + 1);
for (int i = n; i >= 1; i--) {
num[str[i] - 'a']++;
if (num[str[i] - 'a'] == 1) {
vis[++cnt] = str[i] - 'a';
}
}
len = 0;
reverse(vis + 1, vis + 1 + cnt);
for (int i = 1; i <= cnt; i++) {
len += num[vis[i]] / i;
}
if (judge(len)) {
for (int i = 1; i <= len; i++) {
putchar(str[i]);
}
putchar(' ');
for (int i = 1; i <= cnt; i++) {
putchar(char(vis[i] + 'a'));
}
puts("");
}
else {
puts("-1");
}
for (int i = 0; i < 26; i++) {
num[i] = 0;
}
cnt = 0;
}
return 0;
}
F. Nearest Beautiful Number
e a s y h a r d easy\ hard easy hard的做法都是一样的,考虑数位 d p dp dp:
我们定义 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k],表示第 i i i位前已用数字的状态是 j j j,后面还剩下 k k k位不同的数字可用, j j j是一个最大值为 2 10 − 1 2 ^{10} - 1 210−1的二进制数。
通过定义可以发现这个状态对于不同的给定的 K K K都是没有影响的,所以可以不用每次做都去 m e m s e t memset memset,直接 d p dp dp即可。
或者我们可以考虑定义 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k],表示第 i i i位前医用数字的状态是 j j j,总的可用的不同数位是 k k k,然后根据不同的 K K K去转移也可。
最后,对于给定的 n , K n, K n,K,我们先数位 d p dp dp得到 x ≤ n − 1 x \le n - 1 x≤n−1,且最多有 K K K位不同的数字有多少个,然后二分数位 d p dp dp去检验答案。
整体复杂度 O ( T × 10 × log n + 10 × 1024 × 10 ) O(T \times 10 \times \log n + 10 \times 1024 \times 10) O(T×10×logn+10×1024×10)。
#include <bits/stdc++.h>
using namespace std;
int f[15][1050][15], p[15], tot, n, k;
int dfs(int pos, int cur, int last, int flag, int lim) {
if (last < 0) {
return 0;
}
if (!pos) {
return !lim;
}
if (!flag && !lim && f[pos][cur][last] != -1) {
return f[pos][cur][last];
}
int ans = 0, nex = flag ? p[pos] : 9;
for (int i = 0; i <= nex; i++) {
if (lim) {
if (i == 0) {
ans += dfs(pos - 1, cur, last, 0, 1);
}
else {
int mins = cur >> i & 1 ? 0 : 1;
ans += dfs(pos - 1, cur | (1 << i), last - mins, flag && i == nex, 0);
}
}
else {
int mins = cur >> i & 1 ? 0 : 1;
ans += dfs(pos - 1, cur | (1 << i), last - mins, flag && i == nex, 0);
}
}
if (!flag && !lim) {
f[pos][cur][last] = ans;
}
return ans;
}
int calc(int x) {
tot = 0;
while (x) {
p[++tot] = x % 10;
x /= 10;
}
return dfs(tot, 0, k, 1, 1);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
memset(f, -1, sizeof f);
int T;
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &k);
int cur = calc(n - 1);
int l = n, r = 2000000000;
while (l < r) {
int mid = 1ll * l + r >> 1;
if (calc(mid) > cur) {
r = mid;
}
else {
l = mid + 1;
}
}
printf("%d\n", l);
}
return 0;
}