Balala Power!
Time Limit: 4000/2000 MS (Java/Others) Memory Limit:131072/131072 K (Java/Others)
Total Submission(s): 916 Accepted Submission(s): 153
Problem Description
Talented Mr.Tang hasn strings consisting of only lower case characters. He wants to charge them with Balala Power (he could change each character ranged froma toz into each number ranged from0 to25, but each two different characters should not be changed into the same number) so that he could calculate the sum of these strings as integers in base 26 hilariously.
Mr.Tang wants you to maximize the summation. Notice that no string in this problem could have leading zeros except for string "0". It is guaranteed that at least one character does not appear at the beginning of anystring.
The summation may be quite large, so you should output it in modulo 109+7.
Input
The input contains multiple test cases.
For each test case, the first line contains one positive integers n, the number of strings. (1≤n≤100000)
Each of the next n lines contains a stringsi consisting ofonly lower case letters. (1≤|si|≤100000,∑|si|≤106)
Output
For each test case, output "Case #x:y"in one line (without quotes), wherex indicates thecase number starting from 1 andy denotes the answer of corresponding case.
Sample Input
1
a
2
aa
bb
3
a
ba
abc
Sample Output
Case #1: 25
Case #2: 1323
Case #3: 18221
【题意】给出n个由小写字母组成的字符串,你可以对每个字符赋值,范围为0~25,但每个权值只能用一次,且赋值后数字不能有前导零(可以是单个零),然后构成了n个26进制数,问n个数的最大和为多少?
【思路】显然我们应该对位置靠前的字母赋以更大的权值,那么我们首先要做的是,统计出每个字母在哪一位出现了几次,然后比较每个字母取能对结果产生的贡献大小。比赛时有点傻,没办法用大数直接暴力算出每个字母贡献的大小,然后排序,果断超时。
比完后才知道我们可以这样来比较,每个字母从最后一位开始,如果个数大于等于26,则向前进位,以此类推,最终只要从最高位的个数开始比较即可。
然后按贡献从大到小对字母依次用25——0赋值,上面的这个结论是错误的,没有考虑前导零,在读入的时候应用vis数组保存不能赋值为0的字母编号,然后排序后选贡献最小且满足条件的的赋值为0,其他就按贪心赋值,就可以算出结果。
其他细节见代码。
#include <cstdio>
#include <cmath>
#include <vector>
#include <iostream>
#include <set>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)
typedef long long ll;
const int maxn = 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f;
const double eps = 1e-9;
int n;
char s[maxn];
int num[26][maxn];
int vis[26];
int val[maxn];
ll po[maxn];
int Max;
struct node
{
char xx[maxn];
int id;
}e[26];
bool cmp(const node &a,const node &b) //对贡献从大到小排序
{
for(int i=Max-1; i>=0; i--)
{
if(a.xx[i]>b.xx[i])
return 1;
if(a.xx[i]<b.xx[i])
return 0;
}
return 1;
}
void init() //预处理26^n%mod
{
po[0]=1;
for(int i=1; i<maxn; i++)
{
po[i]=(po[i-1]*26)%mod;
}
}
int main()
{
int n;
int cas=1;
init();
while(~scanf("%d",&n))
{
mst(num,0);
mst(vis,0);
mst(val,0);
for(int i=0; i<n; i++)
{
scanf("%s",s);
int len=strlen(s);
if(len>1) //标记这个字母权值不能为零
{
vis[s[0]-'a']=1;
}
for(int j=len-1; j>=0; j--) //统计每个字母在第几位上有几个
{
int o=len-1-j;
num[s[j]-'a'][o]++;
}
Max=max(Max,len); //减少循环,节约时间
}
for(int i=0; i<26; i++)
{
for(int j=0; j<maxn; j++)
{
if(num[i][j]>=26&&j!=Max-1)
{
num[i][j+1]+=num[i][j]/26;
num[i][j]%=26;
}
}
}
for(int i=0; i<26; i++)
{
for(int j=0; j<Max; j++)
{
e[i].xx[j]=num[i][j]+'a'; //转化为字符形式,节约内存,如果是int可能会爆内存
}
e[i].id=i;
}
sort(e,e+26,cmp);
int pos=-1;
for(int i=25;i>=0;i--) //从权值小的开始找权值可以为0的字母
{
if(vis[e[i].id]==0)
{
pos=e[i].id;
break;
}
}
int flag=0;
for(int i=0;i<26;i++) //给每个字母附上权值
{
if(e[i].id==pos)
{
val[e[i].id]=0;
flag=1;
continue;
}
if(flag==0) val[e[i].id]=25-i;
else val[e[i].id]=25-i+1;
}
ll ans=0;
for(int i=0;i<26;i++)
{
for(int j=0;j<Max;j++)
{
if(num[i][j])
{
ll temp=(ll)num[i][j]*val[i]*po[j]%mod;
ans=(ans+temp)%mod;
}
}
}
printf("Case #%d: %I64d\n",cas++,ans);
}
return 0;
}