记录一个菜逼的成长。。
2017 Multi-University Training Contest - Team 1
题目链接
题目大意:
给你n个小写的字符串。
给每种小写字母赋值,每个字符串就相当于一个数,使得最后的和最大。
考虑每种字母的贡献,
如果一个字母贡献大,那么这个字母必然出现在高位且次数多。
根据这个规则排序
对次数考虑进位,可以使常数变小。
再考虑不能给出现过字符串头的字母赋值
0
<script type="math/tex" id="MathJax-Element-4">0</script>。
#include <bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for( int i = l; i <= r; i++ )
#define rep0(i,l,r) for( int i = l; i < r; i++ )
#define ALL(v) (v).begin(),(v).end()
#define cl(a,b) memset(a,b,sizeof(a))
#define clr clear()
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long LL;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
const int maxn = 100000 + 10;
int cnt[27][maxn],head[27],a[27],mxlen;
LL Power[maxn],sum[27];
char str[maxn];
bool cmp(int a,int b)
{
for( int i = mxlen-1; i >= 0; i-- ){
if(cnt[a][i] != cnt[b][i])
return cnt[a][i] < cnt[b][i];
}
return 0;
}
int main()
{
Power[0] = 1;
for( int i = 1; i < maxn; i++ ){
Power[i] = Power[i-1] * 26 % MOD;
}
int n,cas = 1;
while(~scanf("%d",&n)){
cl(cnt,0);cl(sum,0);cl(head,0);
mxlen = 0;
for( int i = 1; i <= n; i++ ){
scanf("%s",str);
int len = strlen(str);
if(len > 1)head[str[0]-'a'] = 1;
mxlen = max(mxlen,len);
for( int j = len-1; j >= 0; j-- ){
cnt[str[j]-'a'][len-j-1] ++;
sum[str[j]-'a'] += Power[len-j-1];//预处理假设所有字母值为1,最后只要乘上给每种字母的权值即可
if(sum[str[j]-'a'] >= MOD)sum[str[j]-'a'] -= MOD;
}
}
//对次数进位,减小常数
for( int i = 0; i < 26; i++ ){
for( int j = 0; j < mxlen; j++ ){
cnt[i][j+1] += cnt[i][j] / 26;
cnt[i][j] %= 26;
}
while(cnt[i][mxlen]){
cnt[i][mxlen+1] += cnt[i][mxlen] / 26;
cnt[i][mxlen++] %= 26;
}
a[i] = i;
}
sort(a,a+26,cmp);
int zero = -1;
//在字符串头部出现过的字母不能赋值0,从小往大遍历,找到可以赋值0,且贡献最小的字母
for( int i = 0; i < 26; i++ )
if(!head[a[i]]){zero = a[i];break;}
LL ans = 0;
for( int x = 25,i = 25; i >= 0; i-- ){
if(a[i] != zero){
ans = (ans + (LL)x-- * sum[a[i]]) % MOD;
}
}
printf("Case #%d: %lld\n",cas++,ans);
}
return 0;
}