传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6034
多校第一场B题
题意:
把字符串每个字符对应0~25的数,进制是26进制,求最大贡献。最大的数匹配 25,次大的数匹配 24,依次类推。排序后这样依次贪心即可,唯一注意的是不能出现前导 0。
下面说下必要的几点处理:
1.前导0,如果排序出来最小贡献的那个数,且现在出现了1~25的赋值,选0的时候,这个字符如果出现在某个串的第一个,那么这个字符就不能赋值为0.
这时候要取到非首字母的第一个最小的数为0。
2.排序,如果直接num[][],在这个题里面超时和超内存,这里可以把这个二维数组映射到一个a[26],num[i][j]字符i位数为j的个数,a[]---贡献字符,sum[a[]]---当前贡献字符的贡献,a[26]为按照贡献排序的进制的26个字符的不同贡献。
3.进位的的处理,如果当前num[i][j]的个数超过26个,位数要进1,特判到达最高位,最长L要+1。
4.取模的处理。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100020;
const int Q = 1e9 + 7;
int n , L;
int num[26][N];//num[i][j]字符i位数为j的个数
int power[N] , sum[N];//sum[i]为i字母的基数进制和,乘以本身值即结果
bool ban[26];
char str[N];
int a[26];
//从小到大
bool cmp(int A , int B) {
//从高位开始比,位数高的肯定大一些
for (int i = L - 1 ; i >= 0 ; -- i) {
if (num[A][i] != num[B][i]) {
return num[A][i] < num[B][i];
}
}
return 0;
}
void work() {
memset(num , 0 , sizeof(num));
memset(ban , 0 , sizeof(ban));
memset(sum , 0 , sizeof(sum));
L = 0;
for (int i = 0 ; i < n ; ++ i) {
scanf("%s" , str);
int len = strlen(str);
//字符串长度等于1的时候随便了
if (len > 1) {
//标记这个字符,这个串的第一个字符不能为0
ban[str[0] - 'a'] = 1;
}
//abc,第一个遍历的a,倒过来就是cba,a下标2,便于加上对应的基数
reverse(str , str + len);
for (int j = 0 ; j < len ; ++ j) {
++ num[str[j] - 'a'][j];
sum[str[j] - 'a'] += power[j];
if (sum[str[j] - 'a'] >= Q) {
sum[str[j] - 'a'] -= Q;
}
}
L = max(L , len);
}
//进位
for (int i = 0 ; i < 26 ; ++ i) {
for (int j = 0 ; j < L ; ++ j) {
num[i][j + 1] += num[i][j] / 26;
num[i][j] %= 26;
}
while (num[i][L]) {
num[i][L + 1] += num[i][L] / 26;
num[i][L ++] %= 26;
}
a[i] = i;
}
sort(a , a + 26 , cmp);
int zero = -1;
for (int i = 0 ; i < 26 ; ++ i) {
//不能非0的最后一个字符赋值为0
if (!ban[a[i]]) {
zero = a[i];
break;
}
}
int res = 0 , x = 25;
//x是从最大开始为字母赋值
for (int i = 25 ; i >= 0 ; -- i) {
if (a[i] != zero) {
res += (LL)(x --) * sum[a[i]] % Q;
res %= Q;
}
}
static int ca = 0;
printf("Case #%d: %d
" , ++ ca , res);
}
int main() {
// freopen("in.txt","r",stdin);
power[0] = 1;
for (int i = 1 ; i < N ; ++ i) {
power[i] = (LL)power[i - 1] * 26 % Q;
}
while (~scanf("%d" , &n)) {
work();
}
return 0;
}
我们队改进后的写法:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
const ll Mod = 1e9 + 7;
int len,ltt[26][maxn];
char s[maxn];
bool apr[26];
ll pow26[maxn];
void init()
{
int len = strlen(s);
for(int i = 0; i < len; i++)
ltt[s[i]-'a'][len - 1 - i]++;
}
bool cmp(int A, int B)
{
for (int i = len - 1 ; i >= 0 ; -- i)
{
if (ltt[A][i] != ltt[B][i])
return ltt[A][i] < ltt[B][i];
}
return 0;
}
int main()
{
// freopen("in.txt","r",stdin);
int n;
int kase = 1;
pow26[0] = 1;
for(int i = 1; i < maxn; i++)
pow26[i] = pow26[i-1]*26%Mod;
while(~scanf("%d", &n))
{
memset(apr, 0, sizeof(apr));
memset(ltt, 0, sizeof(ltt));
len = 0;
for(int i = 0; i < n; i++)
{
scanf("%s",s);
int len1 = strlen(s);
if(len1 > 1) apr[s[0] - 'a'] = 1;
len = max(len,len1);
init();
}
int a[26]; //a数组是ltt数组的映射
//进位
for(int i = 0; i < 26; i++)
{
for(int j = 0; j < len; j++)
{
ltt[i][j+1] += ltt[i][j]/26;
ltt[i][j] %= 26;
}
while(ltt[i][len])
{
ltt[i][len+1] += ltt[i][len]/26;
ltt[i][len++] %= 26;
}
a[i] = i;
}
sort(a, a + 26, cmp);
int num[26] = {0};
if(len > 1)
{
bool flag = 1;
int v = 1;
for(int i = 0; i < 26; i++)
{
if(flag && !apr[a[i]])
{
num[i] = 0;
flag = 0;
}
else num[i] = v++;
}
}
else
{
for(int i = 0; i < 26; i++)
num[i] = i;
}
ll res = 0;
for(int i = 0; i < 26; i++)
for(int j = 0; j < len; j++)
res = (res + num[i]*ltt[a[i]][j]*pow26[j])%Mod;
printf("Case #%d: %I64d
", kase++, res);
}
return 0;
}