补题、杂题N合1

最近好多题没补,慢慢来吧。包括一些开火车和一些$atcoder$,有一些题队友@yangdavid已经写过了,我就不用再浪费时间了。由于不是训练实录,有些水题就不写了

待补任务:

1.$opentrains1538GJ$啃$std$

opentrains 1483 E.Expected LCP

题目:有$n$个长度无穷的$0/1$串
$$
f_n=max_{i \ne j}{LCP(s_i,s_j)}
$$
求$f_n$的期望。

solution:

首先,我们可以确定一个$p\ge1$,使得存在一种方式使得$f_n<p$

这样的话,说明$f_n$至少是$p-1$先把这部分加上去

考虑,$P(i)=P(f_n=i)=\frac{A^n_{2^{i+1}}}{2^{(i+1)n}}-\frac{A^n_{2^{i}}}{2^{in}}$,设$b_i=\frac{A^n_{2^{i}}}{2^{in}}$
$$
E(f_n)=contribution(p-1)+\sum_{i=p}^{\infty}i(b_{i+1}-b_i)=contribution(p-1)-ib_i-\sum_{i={p+1}}^{\infty}b_i
$$
考虑如何计算$\sum_{i={p+1}}^{\infty}b_i$,设$x_i=2^i$
$$
b_i=\frac{\prod_{j=0}^{n-1}x_i-j}{x_i^n}=F(\frac{1}{x_i})
$$
其中$F(x)=\sum_{i=0}^na_ix^i$是一个$n$次多项式

对于每一个$i$,$B_i=\sum_{j=p+1}^{\infty}a_i\frac{1}{x_j^i}$都是一个无穷等比数列求和形式,将每个$B_i$都求出来,则$\sum_{i={p+1}}^{\infty}b_i=\sum_{i=0}^nB_i$。由于$n$有限,因此是对的

启示:当遇到难以处理的数列求和时,可考虑把数列看成多项式形式,对每一项分别求和

opentrains 1538 I.Modulo-magic squares

有一个$n^2$的矩阵,矩阵元素是$[0,m-1]$,求有多少个合法矩阵,满足每一行、每一列、主对角线、副对角线的元素和在模$m$意义下都是一个常数$C$?

$solution$:考虑有多少个自由元,然后答案就是$m^{自由元个数}$

自由元个数:$1(选择C)+(n-1)(n-2)(随意选择前n-2行的前n-1列的每个数字)+(n-3)(选择倒数第二行的除第一列、最后一列、第三列的所有元素)=n^2-2n$

这样,我们用第二项中的$n^2-3n+2$个自由元确定了前$(n-2)$行,用倒数第二行的第二列和倒数第二列两个自由元确定了主副对角线和第一、二、倒数第一、倒数第二列。然后我们用倒数第二行的其他自由元确定了倒数第二行和其他所有的列,至于倒数第一行,由于我们已经确保所有列和其他所有行成立,用二次求和法对矩阵所有元素求和即可证倒数第一行也成立

$ans=m^{n^2-2n}$

以上构造方法对$n\ge5$均成立

手推可知对$n\leq3$也成立

对于$n=4$,我们发现,倒数第二行倒数第二列的取值满足一个可写为$2x=K(mod\ m)$的方程。在$m$为奇数时,取$2$的逆元则有唯一解,这时满足上述公式,但在$m$为偶数时,若$K$为偶数则有两个解否则没有解,打表或经过超高校级麻烦的分类讨论可以发现,$K$不可能为奇数。因此,唯一的特例是,当$n=4,2|m$时:

$ans=2m^{n^2-2n}$

Atcoder AGC010

非常棒的一场,三道操作题三道博弈论,很自闭但真的很有趣,由于官方题解很详细了就不再赘述。

opentrains1519

A.线段树优化建图+强连通分量,B.签到,D.递归优化 F.二维数点 G.最小圆覆盖 H.状压 见@yangdavid博客,不再重新叙述

其中$H$是一道不错的状压

K.Knapsack

这时$Claris$讲课时说的那个随机凸包期望$logn$的随机背包神题,由于也不知道证明,也不是很了解,就不细讲,听$Claris$课即可

I.Statistics

给出 $n≤5000$个物品,第 $i$个物品的体积为$v_i$ ,求所有体积和为$1≤V≤5000$且物品数量最小的集合中,最小的平均体积,最小的中位数(偶数的中位数是中间靠左的一个),最小的众数个数,最小的极差

下面我们一个一个说:

首先我们得做一遍普通背包得知体积恰为$V$最少多少个物品,设为$m$个

平均值:直接算,$O(1)$

中位数:

算法一:做前缀背包和后缀背包并枚举中位数,检查是否合法时枚举集合种比中位数小的背包体积和,复杂度$nV$,常数为$3$

算法二:二分中位数,把每个物品价值变成$inf+(-1)^{[v_i<mid]}$,做体积固定价值最小的背包,若结果大于$m·mid$则当前中位数过小,否则过大。此方法虽然多一个$log$,但巧妙地通过$inf$把物品数量最小的限制转化掉了,感觉挺有启发

众数:

算法一:二分答案然后做背包,复杂度$O(nVlogn)$

算法二:从小到达枚举众数,每次把可以增加的物品加入背包,复杂度$O(nV)$

极差:

极差是这道题目的精华,两个算法都很有启发性

算法一:将物品按照$v_i$排序后,考虑滑动窗口,滑动窗口里是要满足存在一个大小为$m$的子集使得体积和为$V$,我们现在这样支持添加物品和删除物品:维护两个栈,栈的每个元素都是一个长度为$V$的代表背包的数组,共需要$V^2$空间,当我们加入一个物品时,直接加在$A$栈栈顶,用原来$A$栈栈顶的背包更新构成新栈顶,如果我们要删除一个物品(这个物品是当前区间的第一位),这时如果栈$B$不为空,说明要删除的东西是栈$B$栈顶,那么直接将栈$B$的栈顶弹出即可,如果栈$B$是空的,说明要删除的物品是栈底,这时,我们将$A$除了栈底以外的元素一个个弹出,并按照弹出的顺序一个个加入栈$B$并做背包即可。

我们发现,栈$A$从顶到底元素的$v_i$减小,栈$B$从底到顶元素的$v_i$减小

要查询整个背包某一项,直接合并$A、B$栈顶所代表的背包即可,合并一次复杂度$O(V)$

势能分析可得这样做维护两个栈背包的时间是$O(nV)$

以上方法可以处理所有滑动窗口背包、$n$次查询背包某一个值的问题

算法二:按照$v_i$从小到大加入背包,把物品价值设为$inf-v_i$(更新$dp[v_i]$时),$inf$(其他时候),枚举最大值直接查询即可,不得不说这个方法也很棒。

总复杂度$O(nV)$

C.Flip a coin

有两个人,他们分别拥有 $0/1$ 串$s$ 和$t$。现在不停地掷一枚均匀的硬币,正面为 $1$,反面为 $0$。如果某一时刻硬币组成的串中包含 $s$,那么第一个人赢;如果包含 $t$,那么第二个人赢;如果同时包含$s,t$,那么平局。问这三种情况的概率。

字符串长度小于$50$

$solution$:游戏不停止$0/1$串就会不停写下去

算法一:我们设一个零一串$S$的“产生价值”为$f(S)=2^{-|S|}$

求出两个串的前缀$border$数组

我们设$dp[i][j]$表示所有内部不包括$s/t$为子串,且$s、t$的后缀$KMP$状态分别是$i,j$的$0/1$串的价值和

然后我们做高斯消元即可,复杂度$O(n^6)$

算法二:跟上一个算法一样,不过这次,我们建出$s、t$的$AC$自动机,我们的$dp$状态是:所有$s/t$都没有出现过,且后缀状态在$AC$自动机的特定节点上的字符串的价值和,也是一样的高斯消元,只是这次的复杂度为$O((n+m)^3)$

算法三:这道题原题是让输出小数,因此我们怕高斯消元精度不够,这时我们还是考虑$n+m$个$AC$自动机的状态,用他们之间的转移构造出矩阵,跑一个非常大的矩阵快速幂来逼近精确值即可(可理解为串是有限长的,但长度非常长)。复杂度$O((n+m)^3log10^{18})$

E.Tree Paths

一个点的点权就是它的编号,求一棵树上有多少条路径使得$max-min=len$

$n\leq10^5$

考虑点分治,每次计数经过分治中心的路径,在每个点分数中$dfs$,求出每个点$x$到分治中心的路径上的$max,min,len$,然后,我们考虑,对这个分治中心的每个儿子的子树里的点维护一个树状数组,对这棵分治树里的所有点维护一个树状数组,这些树状数组以某种需要的值为下标,记录满足该值的元素的数量。在这里,每条我们要考虑的树链都是由两条来自不同子树的树链拼合而成的(要经过分治中心)。为了满足来自不同子树的性质,我们只需要用在全局树状数组中查询的结果,减去在当前子树代表的树状数组中查询的结果,便是合法的结果。以下我们仅叙述要怎么在某个数据结构中查询合法的数量。

我们首先考虑$max$和$min$都处在两条树链中某一条上的情况数,考虑枚举这条又有$max$又有$min$的链,首先他得满足$max-min=len$,我们称这样的链为合法链,这样,我们按照$min$从大到小考虑每个点,如果这个点代表的链是合法链,我们就考虑有多条链$v$,满足$max_v<max$,因此在这里我们的树状数组的下标应为$max$,这一步等价于一个离线二维数点的过程

接下来我们考虑$max$和$min$分别咋链$a,b$上,这样我们就有$max_a-len_a=min_b+len_b$,这样,我们对每条链就有两个属性$f(a)=max_a-len_a,g(a)=min_a+len+a$,我们把每条链的两个属性看成是两个链,分别是一类链和二类链,我们把所有的$2n$个链按照权值排序,由于每个合法的组合都是两个权值相同的、一个一类一个二类两条链组成,我们按照权值一批一批考虑,在每一批中我们都按照$min$从小到大考虑,每枚举到一个一类链,我们去树状数组中查询$max_v<max$的二类链的数量,这样就做完了。

回顾这个解法:首先,我们用全局树状数组和分子树树状数组保证了来自不同子树这一限制,从而使我们可以随意决定枚举点的顺序,其次,在第一步中,二维数点思维含量不高,第二步中,我们首先用权值限制了所需等式,又用按照$min$从小到大枚举这一顺序限制了$min$的大小关系,这样就只有$max$的大小关系需要使用数据结构维护。我们将$1.不同子树 2.min 3.max 4.max-min=len$四个限制最终化简到只需考虑一个限制

复杂度$O(nlog^2n)$

以上为口胡,这次是个例外,代码完成后会将代码给出。

J.Zigzag

求两个数列的最长公共波浪子序列。

数据范围:$n,m\leq5000$

这题一点都不难,第一次听题的时候yd讲错题意了(要不就是我自己听错了

$O(nm)$的$dp$,方法跟进阶指南傻逼题$LCIS$(最长公共上升子序列)一毛一样

关于一类区间mex问题

现要进行如下操作:

$1.$插入/删除一个数

$2.$询问当前数字集合的$mex$

$solution$:我们考虑,假设操作$n$次,那只有$[0,n-1]$的值有用。现在考虑对值域分块,维护每个数字在集合中有几个,每个块有没有满,那么,就可以做到插入/删除$O(1)$,询问一次$O(\sqrt n)$的复杂度

在区间$mex$问题中,我们考虑先对询问用莫队,那么取块大小$k=\frac{n}{\sqrt m}$最优,相当于有$O(n\sqrt m)$次一类操作,$O(m)$次二类操作,复杂度$O(n\sqrt m+m\sqrt n)$

对于树链$mex$,相当于在欧拉序上跑区间$mex$问题

opentrains1505/1491/1483

历史遗留问题:

opentrains1538J Count the sequences

给定四个数$n,b,c,m$求有多少个长度为$m$的整数数列${x}$满足:

$0\leq x_i\leq b^i-c$

$\sum_{i=1}^mx_i\leq n$

数据范围:

$1\leq m\leq 50,2\leq b\leq10^9,-b+2\leq c\leq b-1,n\leq b^{m+1}$

不是很会,大概有一个补集转化数位$dp$的想法,有时间再啃代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#include <set>
#include <map>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <vector>
#include <bitset>
#include <string>
#include <cstdio>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define mk make_pair
#define pb push_back
#define fi first
#define se second
#define REP(i, x, y) for(int i = (int)x; i <= (int)y; i ++)
#define FOR(i, x, y) for(int i = (int)x; i < (int)y; i ++)
#define PER(i, x, y) for(int i = (int)x; i >= (int)y; i --)
#define trace(x) cerr << #x << " " << x << endl;
#define dprintf(...) fprintf(stderr, __VA__ARGS__)
#define dln() fprintf(stderr, "\n")
using namespace std;
typedef long long LL;
typedef long double db;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPI;
const int N = 100005;
const int P = 998244353;
const int inf = 1e9;
const LL Inf = 1e15;

inline int IN(){
char ch = getchar(); int x = 0, f = 0;
while(ch < '0' || ch > '9') ch = getchar(), f = (ch == '-');
while(ch >= '0' && ch <= '9'){
x = (x << 1) + (x << 3) + ch - 48;
ch = getchar();
}
return f ? (-x) : x;
}

inline int Pow(int x, int y, int p){
int an = 1;
for(; y; y >>= 1, x = (LL)x * x % p) if(y & 1) an = (LL)an * x % p;
return an;
}

void renew(int &x, int y){
x = (x + y >= P ? x + y - P : x + y);
}

void setIO(string a){
#ifndef LOCAL
freopen((a + ".in").c_str(), "r", stdin);
freopen((a + ".out").c_str(), "w", stdout);
#else
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
}

template<typename T> inline void chkmin(T &a, const T &b) {if(a > b) a = b;}
template<typename T> inline void chkmax(T &a, const T &b) {if(a < b) a = b;}

int m, b, c;
int len, fac[55];
char str[100005];
int dig[100005], stk[100005], top;

int Mod(){
long long x = 0;
PER(i, len, 1){
x = (x * 10 + dig[i]);
dig[i] = x / b;
x %= b;
}
while(len && dig[len] == 0) len --;
return x;
}

int dp[55][55][55];
int pw[55][55];
int ifac[55];
int ans[55];

void add(){
stk[0] -= c;
int now = 0;
while(stk[now] >= b){
stk[now] -= b;
stk[now + 1] ++;
now ++;
}
top = max(top, now + 1);
}

int fu;
void Minus(){
stk[0] -= c;
int now = 0;
while(now < top && stk[now] < 0){
stk[now] += b;
stk[now + 1] --;
now ++;
}
if(now >= top){
fu = 1;
return;
}
while(top && stk[top - 1] == 0) top --;
if(!top){
fu = 1;
return;
}
}

int cf[55], ncf[55];
int cmod;

void clear_all(){
len = top = 0;
memset(stk, 0, sizeof stk);
memset(str, 0, sizeof str);
memset(dig, 0, sizeof dig);
memset(dp, 0, sizeof dp);
memset(pw, 0, sizeof pw);
memset(ans, 0, sizeof ans);
fu = 0;
memset(cf, 0, sizeof cf);
memset(ncf, 0, sizeof ncf);
cmod = 0;
}

void Main(){

clear_all();

c = 1 - c;
cmod = (c % P + P) % P;
scanf("%s", str + 1);

len = strlen(str + 1);
REP(i, 1, len) dig[i] = str[i] - 48;
reverse(dig + 1, dig + len + 1);
int nmod = 0;
while(len > 0){
stk[top] = Mod();
top ++;
}

PER(i, top - 1, 0){
nmod = ((LL)nmod * b + stk[i]) % P;
}

nmod = (nmod + P - 1 + m) % P;

REP(i, 0, m) pw[0][i] = ifac[i];

REP(i, 1, m){
pw[i][0] = 1;
int ww = Pow(b, i, P);
REP(j, 1, m) pw[i][j] = (LL)pw[i][j - 1] * ww % P;
REP(j, 0, m) pw[i][j] = (LL)pw[i][j] * ifac[j] % P;
}

dp[1][0][0] = 1;
dp[0][0][0] = 1;
REP(i, 1, m){
REP(j, 0, i) REP(k, 0, m){
const int val = dp[i][j][k];
if(!val) continue;
renew(dp[i + 1][j][k], val);
REP(t, 0, m - k){
int ddd = (t & 1 ? P - pw[i][t] : pw[i][t]);
renew(dp[i + 1][j + 1][k + t], (LL)val * ddd % P);
}
}
}

REP(cho, 0, m){
if(fu) break;
int nowmod = nmod;
int nowcho = 0;
if(top - 1 > m){
int xmod = nowmod;
int *xdp = dp[m + 1][cho];
int xx = (cho & 1 ? P - 1 : 1);
REP(a, 0, m){
int gt = (LL)xx * ifac[a] % P;
REP(b, 0, m - a) {
ans[a + b] = (ans[a + b] + ((LL)xdp[b] * gt)) % P;
}
xx = (LL)xx * xmod % P;
}
nmod = (nmod + P - cmod) % P;
if(c > 0) Minus();
else add();
continue;
}
PER(i, top - 1, 0){
if(stk[i] > 1 && i != 0){
int xmod = (nowmod + P - pw[i][1]) % P;
int xcho = nowcho + 1;
if(xcho <= cho){
int *xdp = dp[i][cho - xcho];
int xx = (cho & 1 ? P - 1 : 1);
REP(a, 0, m){
int gt = (LL)xx * ifac[a] % P;
REP(b, 0, m - a) {
ans[a + b] = (ans[a + b] + ((LL)xdp[b] * gt)) % P;
}
xx = (LL)xx * xmod % P;
}
}
}
if(stk[i] > 0){
int xmod = nowmod;
int xcho = nowcho;
if(xcho <= cho){
int *xdp = dp[i][cho - xcho];
int xx = (cho & 1 ? P - 1 : 1);
REP(a, 0, m){
int gt = (LL)xx * ifac[a] % P;
REP(b, 0, m - a) {
ans[a + b] = (ans[a + b] + ((LL)xdp[b] * gt)) % P;
}
xx = (LL)xx * xmod % P;
}
}
}
if(stk[i] > 1) break;
if(stk[i]){
nowmod = (nowmod + P - pw[i][1]) % P;
nowcho += (stk[i] > 0);
}
}
nmod = (nmod + P - cmod) % P;
if(c > 0) Minus();
else add();
}

cf[0] = 1;
memset(ncf, 0, sizeof ncf);
REP(i, 0, m - 1){
memcpy(ncf, cf, sizeof cf);
memset(cf, 0, sizeof cf);
REP(j, 0, i){
cf[j] = (cf[j] + (LL)ncf[j] * (P - i)) % P;
cf[j + 1] = (cf[j + 1] + ncf[j]) % P;
}
}

int res = 0;
REP(i, 0, m) cf[i] = (LL)cf[i] * ifac[m] % P;
REP(i, 0, m) res = (res + (LL)cf[i] * ans[i] % P * fac[i]) % P;
static int cas = 0;
printf("%d\n", res);
}

int main(){
ifac[0] = fac[0] = 1;
REP(i, 1, 53){
ifac[i] = (LL)ifac[i - 1] * Pow(i, P - 2, P) % P;
fac[i] = Pow(ifac[i], P - 2, P);
}

while(scanf("%d%d%d", &m, &b, &c) != EOF) Main();
}

opentrains1538G String transformation

给定一个长度为$10^5$的字符串,你可以随意操作,每次操作可以选择一个字母让他上一位,如$A->a,e->F,z->A$

问能否使得一番操作后字符串总共刚好有$k$个洞。(如$B$有两个洞,$p$有一个洞,$F$没有洞)

这题貌似分了好久种情况,代码也特别长,有时间啃啃

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double dbl;
typedef pair<int, int> pii;
#define mp make_pair
#define pb push_back

const int INF = (int) (1e9 + 7);

string alph;
int cnt[333];
int pos[333];
string C[] = { "CcEFfGHhIiJjKkLlMmNnrSsTtUuVvWwXxYyZz", "AabDdeOoPpQqR", "Bg" };
map<int, char> nr[52];

struct item {
char f, s;
int v1, v2;
int cost;
int add;

item () {}

item (char f, char s, int v1, int v2, int cost, int add) : f (f), s (s), v1 (v1), v2 (v2), cost (cost), add (add) {}

bool operator < (const item &it) const {
if (abs (add) != abs (it.add)) return abs (add) > abs (it.add);
if (cost != it.cost) return cost < it.cost;
return mp (min (f, s), max (f, s)) < mp (min (it.f, it.s), max (it.f, it.s));
}

friend ostream &operator << (ostream &cout, const item &it) {
cout << it.f << ' ' << it.s << ' ' << it.v1 << ' ' << it.v2 << ' ' << it.add << ' ' << it.cost;
return cout;
}
};

inline int getDst (int a, int b) {
int res = pos[b] - pos[a];
if (res < 0) res += alph.size ();
return res;
}

inline void opt (map<int, int> &t, int k, int v) {
auto it = t.find (k);
if (it == t.end ()) t[k] = v;
else it -> second = min (it -> second, v);
}

void pre () {
for (int i = 0; i < 26; i++) {
alph += 'A' + i;
alph += 'a' + i;
}

for (int i = 0; i < 3; i++) {
for (auto c : C[i]) {
cnt[(int)c] = i;
}
}
for (int i = 0; i < (int)alph.size (); i++) {
pos[(int)alph[i]] = i;
}

for (int i = 0; i < (int) alph.size (); i++) {
int C = cnt[(int) alph[i]];
map<int, int> tmp;
for (auto c : alph) {
int df = cnt[(int) c] - C;
opt (tmp, df, getDst (alph[i], c));
}
for (auto it : tmp) {
nr[i][it.first] = alph[(i + it.second) % alph.size ()];
}
}
}

int n;
string s;
ll k;

void change (int id, int shift) {
int v = s[id];
v = pos[v];
v += shift;
v %= alph.size ();
s[id] = alph[v];
}

void load () {
cin >> n >> k;
cin >> s;
}


vector<int> CC;
set<item> Q;
int ans;

void build () {
for (auto c1 : alph) {
for (auto c2 : alph) {
for (auto it1 : nr[pos[(int) c1]]) {
for (auto it2 : nr[pos[(int) c2]]) {
char c3 = it1.second;
char c4 = it2.second;
int cnt1 = cnt[(int) c1] + cnt[(int) c2];
int cnt2 = cnt[(int) c3] + cnt[(int) c4];
int step = cnt2 - cnt1;
if (abs (step) > 2 || !step) continue;
int cost = getDst (c1, c3) + getDst (c2, c4);
item tmp (c1, c2, getDst (c1, c3), getDst (c2, c4), cost, step);
Q.insert (tmp);
}
}
}
}
CC.resize (alph.size ());
for (auto c : s) {
CC[pos[(int) c]]++;
}
}

vector<int> PS[52];

vector<int> change (int id, int shift, int cnt) {
vector<int> res;
while (cnt --> 0) {
int I = PS[id].back ();
PS[id].pop_back ();
change (I, shift);
res.pb (I);
}
return res;
}

void push (int id, vector<int> &ids) {
for (auto it : ids) PS[id].pb (it);
}

void recoveryInc (int diff) {
for (auto it : Q) {
if (it.add < 0) continue;
if (it.add > diff) continue;

int cnt = diff / it.add;
int sh = it.f == it.s && min (it.v1, it.v2) > 0;

if (it.v1) cnt = min (cnt, CC[pos[(int) it.f]] >> sh);
if (it.v2) cnt = min (cnt, CC[pos[(int) it.s]] >> sh);
if (!cnt) continue;

CC[pos[(int) it.f]] -= cnt;
CC[pos[(int) it.s]] -= cnt;

int nf = (pos[(int) it.f] + it.v1) % alph.size ();
int ns = (pos[(int) it.s] + it.v2) % alph.size ();

CC[nf] += cnt;
CC[ns] += cnt;

vector<int> F, S;

if (it.v1) {
F = change (pos[(int) it.f], it.v1, cnt);
}
if (it.v2) {
S = change (pos[(int) it.s], it.v2, cnt);
}
push ((pos[(int) it.f] + it.v1) % alph.size (), F);
push ((pos[(int) it.s] + it.v2) % alph.size (), S);

diff -= cnt * it.add;
}
}

void recoveryDec (int diff) {
for (auto it : Q) {
if (it.add > 0) continue;
if (it.add < diff) continue;

int cnt = diff / it.add;
int sh = it.f == it.s && min (it.v1, it.v2) > 0;
if (it.v1) cnt = min (cnt, CC[pos[(int) it.f]] >> sh);
if (it.v2) cnt = min (cnt, CC[pos[(int) it.s]] >> sh);
if (!cnt) continue;

CC[pos[(int) it.f]] -= cnt;
CC[pos[(int) it.s]] -= cnt;

int nf = (pos[(int) it.f] + it.v1) % alph.size ();
int ns = (pos[(int) it.s] + it.v2) % alph.size ();

CC[nf] += cnt;
CC[ns] += cnt;

vector<int> F, S;

if (it.v1) {
F = change (pos[(int) it.f], it.v1, cnt);
}
if (it.v2) {
S = change (pos[(int) it.s], it.v2, cnt);
}
push ((pos[(int) it.f] + it.v1) % alph.size (), F);
push ((pos[(int) it.s] + it.v2) % alph.size (), S);

diff -= cnt * it.add;
}

}

void recovery (int diff) {
for (int i = 0; i < (int) s.size (); i++) {
char c = s[i];
PS[pos[(int) c]].pb (i);
}
if (diff < 0) recoveryDec (diff);
else recoveryInc (diff);
}

int getAnsInc (int diff) {
//clog << "Inc" << endl;
//clog << diff << endl;
auto tmpC = CC;
int res = 0;
for (auto it : Q) {
if (it.add < 0) continue;
if (it.add > diff) continue;
int cnt = diff / it.add;
int sh = it.f == it.s && min (it.v1, it.v2) > 0;
if (it.v1) cnt = min (cnt, tmpC[pos[(int) it.f]] >> sh);
if (it.v2) cnt = min (cnt, tmpC[pos[(int) it.s]] >> sh);
if (!cnt) continue;
//clog << it << ' ' << cnt << endl;
tmpC[pos[(int) it.f]] -= cnt;
tmpC[pos[(int) it.s]] -= cnt;
int nf = (pos[(int) it.f] + it.v1) % alph.size ();
int ns = (pos[(int) it.s] + it.v2) % alph.size ();
//clog << alph[nf] << ' ' << alph[ns] << endl;
tmpC[nf] += cnt;
tmpC[ns] += cnt;
res += cnt * it.cost;
diff -= cnt * it.add;
}
return res;
}

int getAnsDec (int diff) {
auto tmpC = CC;
int res = 0;
for (auto it : Q) {
if (it.add > 0) continue;
if (it.add < diff) continue;
int cnt = diff / it.add;
int sh = it.f == it.s && min (it.v1, it.v2) > 0;
if (it.v1) cnt = min (cnt, tmpC[pos[(int) it.f]] >> sh);
if (it.v2) cnt = min (cnt, tmpC[pos[(int) it.s]] >> sh);
if (!cnt) continue;
tmpC[pos[(int) it.f]] -= cnt;
tmpC[pos[(int) it.s]] -= cnt;
int nf = (pos[(int) it.f] + it.v1) % alph.size ();
int ns = (pos[(int) it.s] + it.v2) % alph.size ();
tmpC[nf] += cnt;
tmpC[ns] += cnt;
res += cnt * it.cost;
diff -= cnt * it.add;
}
return res;
}

void getAnsOdd (int diff) {
int best = 1e9;
char f = -1, s = -1;
for (auto c : alph) {
if (CC[pos[(int) c]] == 0) continue;
int C = cnt[(int) c];
int bst = 1e9;
char to = -1;
for (auto c2 : alph) {
int df = abs (C - cnt[(int) c2]);
if (!(df & 1)) continue;
if (bst > getDst (c, c2)) {
bst = getDst (c, c2);
to = c2;
}
}
int ans = getDst (c, to);
CC[pos[(int) c]]--;
CC[pos[(int) to]]++;
int CH = cnt[(int) to] - cnt[(int) c];
//clog << "change one : " << c << ' ' << to << endl;
if (diff - CH < 0) ans += getAnsDec (diff - CH);
else ans += getAnsInc (diff - CH);
CC[pos[(int) c]]++;
CC[pos[(int) to]]--;
if (ans < best) {
best = ans;
f = c;
s = to;
}
}
//clog << f << ' ' << s << endl;
CC[pos[(int) f]]--;
CC[pos[(int) s]]++;
int CH = cnt[(int) s] - cnt[(int) f];
diff -= CH;

for (int i = 0; i < (int) ::s.size (); i++) {
if (::s[i] == f) {
change (i, getDst (f, s));
break;
}
}
recovery (diff);
ans = best;
}

void getAns (int diff) {
//clog << diff << endl;
if (diff == 0) return;
if (abs (diff) & 1) {
getAnsOdd (diff);
return;
}
if (diff < 0) ans = getAnsDec (diff);
else ans = getAnsInc (diff);
recovery (diff);
}

void solve () {
if (k < 0 || k > 2 * n) {
cout << -1 << endl;
return;
}

int cur = 0;
for (auto c : s) {
cur += cnt[(int) c];
}

build ();

int diff = k - cur;

getAns (diff);

cout << ans << endl;
cout << s << endl;
}

int main () {
ios_base::sync_with_stdio (false);
cin.tie (NULL);

pre ();
load ();
solve ();

return 0;
}