2017多校 Balala Power!
當時比賽這個題,wa了次,最後發現思路是有一點問題。今天補題,也是出現了很多錯誤,哎,還是太菜了,必須要多寫程式碼。
題意:將a-z這26個字母用0-25進行賦值。那麼對於一個字串,就可以看作是一個26進位制,那麼該字串就可以轉化為一個10進位制的數,求所有字串的和的最大值。(如果一個字母被賦值為0,那麼該字母不能出現在首位。a這種是可以的)
比賽時候的思路是:我把每一個位賦初值0,100000,200000....這種。每個字母出現在每一位的時候就++,然後把所有字母的和相加,可以對這些字母進行排序。
後來想的是通過用num[26][100010],表示該字母在這一位上的個數,如果數量超過26,就進位。
程式碼如下:
//高精度的思想求每個字母的權值
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll num[28][100050];
ll high[27],alph[27],lead[27],weight[27];//high記錄每個字母的最高位是第幾位,alph記錄字母的權值大小,從大到小,lead記錄前導零,weight記錄權值
bool cmp(ll a,ll b)
{
if(high[a]==high[b])
{
for(ll j=high[a];j>=0;j--)
{
if(num[a][j]!=num[b][j])
return num[a][j]>num[b][j];
}
}
else
return high[a]>high[b];
}
long long pow(long long a,long long x)//以防萬一,這裡以後都要有long long
{
long long res=1;
while(x)
{
if(x&1)
res=(res*a)%mod;
a=(a*a)%mod;
x>>=1;
}
return res%mod;
}
int main()
{
ll n,kase=1;
while(~scanf("%lld",&n))
{
char s[100010];
ll lenn=-1;
memset(num,0,sizeof(num));
memset(lead,0,sizeof(lead));
memset(high,0,sizeof(high));
for(int i=0;i<n;i++)
{
scanf("%s",&s);
ll len=strlen(s);
if(len!=1)
lead[s[0]-'a']=1;
for(ll j=len-1;j>=0;j--)
{
num[s[j]-'a'][len-j]++;
if(num[s[j]-'a'][len-j]>=26)//1是最低位
{
num[s[j]-'a'][len-j+1]+=num[s[j]-'a'][len-j]/26;
num[s[j]-'a'][len-j]=num[s[j]-'a'][len-j]%26;
}
}
lenn=max(lenn,len);
}
for(ll i=0;i<26;i++)
{
for(ll j=1;j<lenn+10;j++)
{
if(num[i][j])
high[i]=j;//記錄每個字母的最高位
num[i][j+1]+=num[i][j]/26;
num[i][j]=num[i][j]%26;
}
}
for(ll i=0;i<26;i++)
alph[i]=i;
sort(alph,alph+26,cmp);
int k=25;
for(int i=0;i<26;i++)
weight[alph[i]]=k--;
if(weight[alph[25]]==0&&lead[alph[25]])//如果權值最小的那個字母是首字母
{
for(int i=24;i>=0;i--)
{
if(lead[alph[i]]==0)
{
for(int j=25;j>=i;j--)
{
weight[alph[j]]=weight[alph[j-1]];
}
weight[alph[i]]=0;
break;
}
}
}
long long ans=0;
for(ll i=0;i<26;i++)
{
for(ll j=1;j<lenn+26;j++)
{
ans+=((long long)(pow(26,j-1)*weight[i])%mod*num[i][j])%mod;
ans=ans%mod;
}
}
printf("Case #%lld: %lld\n",kase++,ans);
}
}