【2017多校】第一场B题:Balala Power!

题目:

Balala Power!

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: >131072/131072 K (Java/Others)
Total Submission(s): 4809 Accepted Submission(s): 387

Problem Description

Talented Mr.Tang has n strings consisting of only lower case ?>characters. He wants to charge them with Balala Power (he could >change each character ranged from a to z into each number >anged from 0 to 25, 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 any string.

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 string si consisting of only >lower case letters. (1≤|si|≤100000,∑|si|≤106)

Output
For each test case, output “Case #x: y” in one line (without >quotes), where x indicates the case number starting from 1 and y >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

思路及心路历程:

一开始并没有读懂题目,sjy说了一句26进制。然后壮壮拿笔刷刷刷写了几个数,加起来正好是样例的输出。
读懂题目之后开始思考算法。我和壮壮的第一个想法是,把最长的那一串的首字母赋最大值,即25,这样最大化利用位权。但我马上想到一种情况,次高位有27个没有在最高位出现过的字母,这样则用数量抵消了位权。或者次次高位有 2726 个一样没在次高位出现的字母。最多有十万位,不可能这样一直找下去。首先会超范围,再就是会浪费时间。
想到这种情况之后,我们假设一位位找下去,确定了一个字母的最优方案,但是却没有考虑到其他字母的最优方案。即只考虑了局部最优解,而没有顾及整体的利益。
就在我们一筹莫展的时候,超超提出,对于一个字母,可以从最低位开始,每出现26次即向高位进位,这样即可获得在既考虑到每个字母出现次数,又考虑到位权的情况下,获得每个字母的“特征值”。
之后这道题由壮壮开始码。首先用一个 26×100000 的数组,存下每个字母在每一位出现了多少次。然后进行进位计算,得到类似如下所示的一张表。
字母
f01220
a121430
b1571511
然后对这张表进行排序。因为实现是用的二维数组,每个字母对应一行,所以无法用STL里自带的sort函数进行排序,只好自己手写。用的是冒泡排序,从最高位的权值开始比较,若当前位权值相同,则往下一位找。
可以用 swap() 函数交换两行。
提前打好位权的表,数据类型要用 longlong ,同时要遵照题意取模。
这时考虑前导零的情况,题意保证,必有至少一个字符不是其他任意字符串的前缀。这就保证了,最坏情况下, 0值也是可以被赋出去的。
对每一个输入进来的字符串进行遍历,若从第二个字母开始,所有的字母都和头字母相同,则这个字母是可以被赋0的。
但还要小心先出现 aab ,后出现 aaa 的情况,不要让后面这种情况把原先记录的a不能被赋0给顶替掉。
期间七七八八改了8、9次,最后五分钟我从头检查了一遍,改了一个小细节,然后A掉了。所以就算到最后也不要放弃,从头再把代码看一遍,也许会有收获。

代码

/***********************
author : Meng Xiangcong
***********************/
//#include <bits/stdc++.h>
#include <map>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <utility>
#include <climits>
#include <ctype.h>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;

#define PI         acos(-1)
#define INF        (1<<30)
#define EPS        1e-6
#define SETDATA    memset(data, 0, sizeof(data))
#define SET(a, b)  memset(a, b, sizeof(a))
typedef long long LL;

const int mod = 1e9 + 7;
int x[26][100000 + 5];

int main()
{
    long long poww[100000 + 5] = {1, 0};
    for(int i = 1; i <= 100000; ++i)
    {
        poww[i] = 26 * poww[i - 1];
        poww[i] %= mod;
    }

    int n;
    char str[100000 + 5];
    int maxlen, len;
    long long sum;
    int xi[26];
    int xx = 1;
    int xxx[26];
    while(~scanf("%d", &n))
    {
        maxlen = 0;
        sum = 0;
        memset(x, 0, sizeof(x));
        memset(xi, 0, sizeof(xi));
        memset(xxx, 0, sizeof(xxx));
        for(int i = 0; i < 26; ++i)
        {
            x[i][100002] = i;
        }
        while(n--)
        {
            int flagg = 0;
            scanf("%s", str);
            len = strlen(str);
            if(len > maxlen) maxlen = len;
            if(len > 1)
            {
                for(int i = 0; i < len - 1; ++i)
                {
                    if(str[i] != str[i + 1])
                    {
                        flagg = 1;
                        break;
                    }
                }
            }

            if(flagg) xi[str[0]-'a'] = 1;// no

            int i,j;
            for(i = len - 1, j = 0; i >= 0; --i, ++j)
            {
                ++x[str[i] - 'a'][j];
            }
        }
        for(int i = 0; i < 26; ++i)
        {
            for(int j = 0; j <= maxlen; ++j)
            {
                x[i][j + 1] += x[i][j] / 26;
                x[i][j] %= 26;
            }
        }
        for(int ii = 0; ii < 25; ++ii)
        {
            for(int j = 0, k = maxlen + 1; j < 25; ++j, k = maxlen)
            {
                while(x[j][k] == x[j + 1][k] && k != 0)
                {
                    --k;
                }
                if(x[j][k] < x[j + 1][k])
                {
                    swap(x[j], x[j + 1]);
                }
            }
        }
        for(int i = 25; i >= 0;)
        {
            if(xi[x[i][100002]])
            {
                --i;
            }
            else
            {
                for(int j = i; j < 25; ++j)
                {
                    swap(x[j], x[j + 1]);
                }
                break;
            }
        }
        for(int i = 0; i < 26; ++i)
        {
            for(int j = 0; j <= maxlen + 1; ++j)
            {
                sum += (poww[j] * x[i][j] % mod * (25 -  i) % mod);
                sum %= mod;
            }
        }
        printf("Case #%d: %lld\n", xx, sum);
        ++xx;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值