题意:
有若干个 (1 <= n <= 100000) 只含小写字母的字符串, (|s| <= 100000)
现将小写字母映射到 0 ~ 25 这 26 个数字,保证映射为单射,得到若干个 26 进制的数字
要求这若干个数字加起来的和最大,求最大值 mod (1e9+7)
/*
被 Balala Power 坑哭
我要 Nozomi Power
嘤嘤嘤
*/
思路:
对于每一个字母,考虑它对最终总和的贡献,按贡献从高到低而从25开始由大到小赋值,比如说 a 的贡献最大,就给 a 赋 25;f 次大,给 f 赋 24;以此类推
但是是二十六进制,且串的长度有 100000,太大了,不可能将每个字母的贡献用十进制的一个数存下来然后比较大小,
因此可以考虑就将其以二十六进制的模样呈现出来,进行比较,
什么意思呢?
对于每一个字母,开一个长度为 100000 的数组,该字母在哪个位置上出现过,就将该位置的值 + 1,
(其实就相当于把该字母的贡献用一个 26 进制的数表示了出来,不过该数的每一位都是一个数组元素罢了)
这样之后,排个序,赋个值,皆大欢喜
然鹅
不坑一点还叫做题目吗
题目中要求不能出现前导 0,我们上面讲了那么多,可是压根没有考虑前导 0 的问题哇
题目保证至少有一个字母没有在首位出现过,因此肯定可以保证至少有一种赋值方法满足不存在前导 0,
处理起来其实也很方便,
刚刚已经把贡献从大到小排了序,现在从小往大找,找到第一个没有出现在首位过的字母,就把 0 赋给它
搞定
然鹅
题目还说了,允许单个的 ‘0’
(请读者自动脑补气哭表情包)
那么再从小往大找,找到第一个长度为 1 的,和刚刚上面找到的那个比较一下,取贡献最小的那个,将 0 赋给它
这样
总算是搞定了
然鹅
毕竟可是一直到结束都没有过的
还出了些小问题
比如说
1.
排序的时候忘记了将 exist 数组也一并交换,导致一开始记录的哪个字母在首位出现过的信息在后面要用的时候就混乱了...
(其实一开始还忘记了交换 lenn 数组...)
后来学姐说,这些东西就应该统一丢到一个 struct 里面,就不会漏了
是啊以后要长记性了
2.
说好的 26 进制,你写进位了吗?
没有...。
AC代码如下:
#include <cstdio>
#include <cmath>
#include <cstring>
#define mod 1000000007
#define maxn 100000
typedef long long LL;
int kas, n, lenn[30], tot[30][maxn + 10];
char s[maxn + 10];
bool exist[maxn + 10];
bool lessthan(int x, int y) {
if (lenn[x] > lenn[y]) return false;
if (lenn[x] < lenn[y]) return true;
int len = lenn[x];
for (int i = len; i >= 0; --i) {
if (tot[x][i] > tot[y][i]) return false;
else if (tot[x][i] < tot[y][i]) return true;
}
return false;
}
LL poww(LL x, LL y) {
LL ret = 1;
while (y) {
if (y & 1) { ret *= x; ret %= mod; }
x *= x;
x %= mod;
y >>= 1;
}
return ret;
}
void swapp(int x, int y) {
for (int i = 0; i <= maxn; ++i) {
int temp = tot[x][i];
tot[x][i] = tot[y][i];
tot[y][i] = temp;
}
int temp = lenn[x];
lenn[x] = lenn[y];
lenn[y] = temp;
bool t = exist[x];
exist[x] = exist[y];
exist[y] = t;
}
LL calc(int zero) {
LL ans = 0, x = 26, xx;
for (int i = 0; i < 26; ++i) {
if (zero == i) xx = 0;
else { --x; xx = x; }
LL mul = 0;
for (int j = 0; j <= lenn[i]; ++j) {
mul += (poww(26, j) * (LL)tot[i][j]) % mod;
mul %= mod;
}
mul *= xx;
mul %= mod;
ans += mul;
ans %= mod;
}
return ans;
}
void work() {
memset(tot, 0, sizeof(tot));
memset(exist, 0, sizeof(exist));
for (int i = 0; i < n; ++i) {
scanf("%s", s);
int len = strlen(s);
for (int j = 0; j < len; ++j) ++tot[s[j] - 'a'][len - j - 1];
exist[s[0] - 'a'] = true;
}
for (int i = 0; i < 26; ++i) {
int j;
for (j = maxn; j >= 0; --j) {
if (tot[i][j] > 0) break;
}
lenn[i] = j;
}
for (int i = 0; i < 26; ++i) {
for (int j = 0; j <= lenn[i]; ++j) {
if (tot[i][j] >= 26) {
tot[i][j + 1] += tot[i][j] / 26;
tot[i][j] %= 26;
}
}
while (tot[i][lenn[i] + 1] > 0) ++lenn[i];
}
for (int i = 0; i < 26; ++i) {
for (int j = i + 1; j < 26; ++j) {
if (lessthan(i, j)) {
swapp(i, j);
}
}
}
// for (int i = 0; i < 26; ++i) {
// for (int j = 0; j <= lenn[i]; ++j) {
// printf("%d ", tot[i][j]);
// }
// if (lenn[i] >= 0) printf("\n");
// }
// printf("%d\n", zero);
int zero = -1;
if (lenn[25] >= 0) {
for (int j = 25; j >= 0; --j) {
if (!exist[j] || lenn[j] == 0) {
zero = j;
break;
}
}
}
LL ans = calc(zero);
printf("Case #%d: %lld\n", ++kas, ans % mod);
}
int main() {
while (scanf("%d\n", &n) != EOF) work();
return 0;
}
啊啰里啰嗦说了这么多qwqq
这么多没考虑的地方这么多写跪的地方qwqq
以及w想要数据的童鞋可以私戳我喵~
啊还有
这种考虑某个个体对整体的贡献的思路感觉真的很重要呢
这次的 03 也是