题目描述
给出一个字符串 s s s,若其两个位置 i , j ( i < j < n ) i, j(i < j < n) i,j(i<j<n)满足 a i = a j & & a i + 1 = a j + 1 a_i = a_j ~~\&\&~~ a_{i+1} = a_{j+1} ai=aj && ai+1=aj+1那么会对该字符串的价值加一,给定 n , k n,k n,k,仅适用前 k k k个字符构造一个长度为 n n n的字符串使得价值最小。
解题思路
这题过的人不少,自己没过的主要原因是没有研究好样例,实际上样例一给出的就是正确构造方式,而我没有研究样例导致一个多小时也没找到正确的构造方式。
在仔细观察后我们可以发现只需要构造出一个最长的0贡献的字符串,然后不断循环它直到长度到达 n n n。可用的对数实际上只有 k ∗ k k*k k∗k对,所以后面出现的每对一定都会产生贡献,为了贡献最小,以这样的方式。具体参考了学长的证明:找到的最长0贡献字符串长度是len,我们把len看作一个周期,那么在下一个周期当中,让贡献速度增加最慢的方式就是让上个周期的每一个相邻位置(记为 p a i r pair pair )都出现唯一一次。在第一个周期中大约有 l e n 2 \frac{len}{2} 2len个pair,第二个周期中每个 p a i r pair pair 都多出现了仅1次,那么贡献就是大约 l e n 2 \frac{len}{2} 2len,如果某个 p a i r pair pair 多出现了一次,另一个 p a i r pair pair 少出现了一次,那么总贡献会更糟糕。
画出一个 k ∗ k k*k k∗k的矩阵时可以发现 a a b a c ⋅ ⋅ ⋅ a z b b c ⋅ ⋅ ⋅ b z c . . . a~ab~ac~···~az~b~bc~···~bz~c... a ab ac ⋅⋅⋅ az b bc ⋅⋅⋅ bz c...这样的构造方式确实是比较容易的构造方式。以后的构造题一定要好好研究样例
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int Mod = 1e9 + 7;
const int maxn = 3e5 + 15;
int a[26][26];
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
string s = "";
while (1) {
for (int i = 0; i < m; i++) {
char cur = 'a' + i;
s += cur;
n--;
if (n == 0) goto done;
for (int j = i + 1; j < m; j++) {
s += cur;
n--;
if (n == 0) goto done;
s += 'a' + j;
n--;
if (n == 0) goto done;
}
}
}
done:
cout << s << ENDL;
return 0;
}