CF946E
为了让我们构造出来的结果尽量大而又不超过给定的 \(s\),当我们构造的串长度跟 \(s\) 相同时,就可以知道存在一个 \(k\) 满足我们构造出来的串前 \(k-1\) 位和 \(s\) 一样,第 \(k\) 位比 \(s_k\) 小,然后后面 \(k+1\) 到 \(n\) 位能使串满足定义又尽量大。
所以我们从高位到低位枚举那个被修改小的位置 \(k\),然后从大到小枚举它被改成的数字。接下来如何判断成立与否了,因为前 \(k-1\) 位我们填的都是 \(s\) 串中对应的数,第 \(k\) 位又是我们枚举出来的,而我们又要满足所有数字出现次数为偶数次,所以前 \(k\) 位出现次数为奇数的数字都得在后面出现奇数次。所以,如果出现为奇数次的数字数量 \(x\) 大于 \(n-k\),则无法构造出来,否则可以。
至于构造方法,就是最后 \(x\) 位从大到小填上那些需要补全为偶数次的数字,中间剩余的位置都填 \(9\) 即可。
统计一段前缀每种数字出现的奇偶次数可以预处理出来,所以总复杂度是线性的。
若找不到满足条件的等长串,则输出 \(n-2\) 个 \(9\)。
Code:
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int T;
int n, m;
int sum[N][10];
char s[N];
void solve() {
scanf("%s", s + 1), n = strlen(s + 1);
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < 10; ++j)
sum[i][j] = sum[i - 1][j] ^ (s[i] - '0' == j);
}
for (int i = n; i; --i)
for (int j = s[i] - '0' - 1; ~j; --j) {
if (i == 1 && j == 0) continue;
int tmp = 0;
for (int k = 0; k < 10; ++k) tmp += sum[i - 1][k] ^ (j == k);
if (tmp <= n - i) {
for (int k = 1; k < i; ++k) putchar(s[k]);
putchar(j + '0');
for (int k = i + 1; k <= n - tmp; ++k) putchar('9');
for (int k = 9; ~k; --k)
if (sum[i - 1][k] ^ (j == k)) putchar(k + '0');
printf("\n"); return;
}
}
for (int i = 1; i <= n - 2; ++i) putchar('9');
printf("\n");
}
int main() {
scanf("%d", &T);
while (T--) solve();
return 0;
}