2016"百度之星" - 资格赛(Astar Round1)【解题报告】

这里将本次资格赛的题目和解题报告放上来,自己也从中学习了复习了不少知识点,做题的感觉不错。

Problem A

 
 Accepts: 1299
 
 Submissions: 9670
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

度熊手上有一本字典存储了大量的单词,有一次,他把所有单词组成了一个很长很长的字符串。现在麻烦来了,他忘记了原来的字符串都是什么,神奇的是他竟然记得原来那些字符串的哈希值。一个字符串的哈希值,由以下公式计算得到:

H(s)=\prod_{i=1}^{i\leq len(s)}(S_{i}-28)\ (mod\ 9973)H(s)=i=1ilen(s)(Si28) (mod 9973)

S_{i}Si代表 S[i] 字符的 ASCII 码。

请帮助度熊计算大字符串中任意一段的哈希值是多少。

Input

多组测试数据,每组测试数据第一行是一个正整数NN,代表询问的次数,第二行一个字符串,代表题目中的大字符串,接下来NN行,每行包含两个正整数aabb,代表询问的起始位置以及终止位置。

1\leq N\leq 1,0001N1,000

1\leq len(string)\leq 100,0001len(string)100,000

1\leq a,b\leq len(string)1a,blen(string)

Output

对于每一个询问,输出一个整数值,代表大字符串从 aa 位到 bb 位的子串的哈希值。

Sample Input
2
ACMlove2015
1 11
8 10
1
testMessage
1 1
Sample Output
6891
9240
88
题解:

这题先打表算出第一个元素到第i个元素的所有值hash[i],所求是x,那么hash[left-1] * x %9973 = hash[right] % 9973,实际上就是解模线性方程模线性方程 a * x = b (% n)。x为正整数。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define max(x, y) ((x) > (y) ? (x) : (y))
#define min(x, y) ((x) < (y) ? (x) : (y))

#define maxn 100010
char szWords[maxn];
int dp[maxn];

int extgcd(int a, int b, int &x, int &y)
{
    if (b == 0) { x = 1; y = 0; return a;}
    int d = extgcd(b, a % b, x, y);
    int t = x; x = y; y = t - a / b * y;
    return d;
}

vector modeq(int a, int b, int n)
{
    vector res;

    int e, i, d, x, y;
    d = extgcd(a, n, x, y);
    if (b % d > 0) return res;

    e = (x * (b / d)) % n;
    for (i = 0; i < d; i++)
    {
        int ans = (e + i *(n / d)) % n;
        res.push_back(ans);
    }

    return res;
}

int main()
{
    //freopen("input.txt", "r", stdin);

    int N;
    while (scanf("%d", &N) != EOF)
    {
        /*getchar();
        gets(szWords);*/
        scanf("%s", szWords);

        int len = strlen(szWords);
        dp[0] = 1;
        for (int i = 0; i < len; ++i)
        {
            dp[i+1] = dp[i] * ((int)szWords[i] - 28);
            dp[i+1] %= 9973;
        }

        for (int i = 0; i < N; ++i)
        {
            int a, b;
            scanf("%d %d", &a, &b);
            vector res = modeq(dp[a-1], dp[b], 9973);
            int ans = res[0];
            if (ans < 0)
            {
                ans += 9973;
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}

Problem B

 
 Accepts: 1729
 
 Submissions: 6485
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

度熊面前有一个全是由1构成的字符串,被称为全1序列。你可以合并任意相邻的两个1,从而形成一个新的序列。对于给定的一个全1序列,请计算根据以上方法,可以构成多少种不同的序列。

Input

这里包括多组测试数据,每组测试数据包含一个正整数NN,代表全1序列的长度。

1\leq N \leq 2001N200

Output

对于每组测试数据,输出一个整数,代表由题目中所给定的全1序列所能形成的新序列的数量。

Sample Input
1
3
5
Sample Output
1
3
8

Hint
如果序列是:(111)。可以构造出如下三个新序列:(111), (21), (12)。
题解:

简单递推,考虑dp[i],那么有两种情况,一种是i不与其他元素合并,有dp[i-1],另一种是一定与其他元素合并,有dp[i-2],即dp[i] = dp[i-1] + dp[i-2],斐波那契数列。

另外需要注意溢出的问题,C++用大数模板。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define max(x, y) ((x) > (y) ? (x) : (y))
#define min(x, y) ((x) < (y) ? (x) : (y))

const int base = 10000;  // (base^2) fit into int
const int width = 4; // width = log base
const int N = 1000; // n * width:  可表示的最大位数
struct bint{
    int ln, v[N];
    bint (int r = 0) { // r 应该是字符串!
        for (ln = 0; r > 0; r /= base) v[ln++] = r % base;
    }
    bint& operator = (const bint& r) {
        memcpy(this, &r, (r.ln + 1) * sizeof(int));// !
        return *this;
    }
} ;

bint operator + (const bint& a, const bint& b){
    bint res; int i, cy = 0;
    for (i = 0; i < a.ln || i < b.ln || cy > 0; i++) {
        if (i < a.ln) cy += a.v[i];
        if (i < b.ln) cy += b.v[i];
        res.v[i] = cy % base; cy /= base;
    }
    res.ln = i;
    return res;
}

bool read(bint& b, char buf[]) //  读取失败返回0
{
    //if (1 != scanf("%s", buf)) return 0;
    int w, u, ln = strlen(buf);
    memset(&b, 0, sizeof(bint));
    if ('0' == buf[0] && 0 == buf[1]) return 1;
    for (w = 1, u = 0; ln; ) {
        u += (buf[--ln] - '0') * w;
        if (w * 10 == base) {
            b.v[b.ln++] = u; u = 0; w = 1;
        }
        else w *= 10;
    }
    if (w != 1) b.v[b.ln++] = u;
    return 1;
}
void write(const bint& v){
    int i;
    printf("%d", v.ln == 0 ? 0 : v.v[v.ln - 1]);
    for (i = v.ln - 2; i >= 0; i--)
        printf("%04d", v.v[i]); // ! 4 == width
    printf("\n");
}

bint num[210];

void InitAns()
{
    read(num[1], "1");
    read(num[2], "2");
    for (int i = 3; i <= 200; ++i)
    {
        num[i] = num[i-2] + num[i-1];
    }
}

int main()
{
    //freopen("input.txt", "r", stdin);

    InitAns();

    int N;
    while (scanf("%d", &N) != EOF)
    {
        write(num[N]);
    }
    return 0;
}

Problem C

 
 Accepts: 502
 
 Submissions: 4403
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 131072/131072 K (Java/Others)
Problem Description

度熊手上有一本神奇的字典,你可以在它里面做如下三个操作:

1、insert : 往神奇字典中插入一个单词

2、delete: 在神奇字典中删除所有前缀等于给定字符串的单词

3、search: 查询是否在神奇字典中有一个字符串的前缀等于给定的字符串
Input

这里仅有一组测试数据。第一行输入一个正整数N (1\leq N\leq 100000)N(1N100000),代表度熊对于字典的操作次数,接下来NN行,每行包含两个字符串,中间中用空格隔开。第一个字符串代表了相关的操作(包括: insert, delete 或者 search)。第二个字符串代表了相关操作后指定的那个字符串,第二个字符串的长度不会超过30。第二个字符串仅由小写字母组成。

Output

对于每一个search 操作,如果在度熊的字典中存在给定的字符串为前缀的单词,则输出Yes 否则输出 No。

Sample Input
5
insert hello
insert hehe
search h
delete he
search hello
Sample Output
Copy
Yes
No

题解:

典型的字典树,纯模板,醉了。

#include 
#include 
#include 
using namespace std;

#define maxnode 30

struct Node
{
    char ch;
    Node *next[maxnode];
    int count;

    Node()
    {
        count = 0;
        memset(next, 0, sizeof(next));
    }
};

void Destroy(Node *root)
{
    if (!root) return;

    for (int i = 0; i < maxnode; ++i)
    {
        Destroy(root->next[i]);
    }
    delete root;
}


void DestroySon(Node *root)
{
    if (!root) return;

    for (int i = 0; i < maxnode; ++i)
    {
        Destroy(root->next[i]);
        root->next[i] = NULL;
    }
}

void Insert(Node *root, char str[])
{
    Node *cur = root;
    for (int i = 0; str[i]; ++i)
    {
        int id = str[i] - 'a';
        if (!cur->next[id]) cur->next[id] = new Node;

        cur = cur->next[id];
        ++cur->count;
    }
}

Node *Search(Node *root, char str[])
{
    Node *cur = root;
    for (int i = 0; str[i]; ++i)
    {
        int id = str[i] - 'a';
        if (cur->next[id])
            cur = cur->next[id];
        else
            return NULL;
    }

    return cur;
}

void Delete(Node *root, char str[])
{
    Node *cur = Search(root, str);
    if (!cur) return;

    DestroySon(cur);
    cur->count;

    Node *pcur = root;
    for (int i = 0; str[i]; ++i)
    {
        int id = str[i] - 'a';
        if (pcur->next[id])
        {
            pcur = pcur->next[id];
            pcur->count -= cur->count;
        }
    }
}

int main()
{
    //freopen("input.txt", "r", stdin);

    int N;
    scanf("%d",&N);
    Node *root = new Node;
    for(int i = 0; i < N; ++i)
    {
        char op[32],s[32];
        scanf("%s %s", op, s);
        if(op[0] == 'i')
        {
            Insert(root, s);
        }
        else if(op[0] == 'd')
        {
            Delete(root, s);
        }
        else
        {
            Node *tmp = Search(root,s);
            if (tmp && tmp->count > 0)
            {
                printf("Yes\n");
            }
            else
            {
                printf("No\n");
            }
        }
    }
    return 0;
}

Problem D

 
 Accepts: 1897
 
 Submissions: 5482
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

度熊所居住的 D 国,是一个完全尊重人权的国度。以至于这个国家的所有人命名自己的名字都非常奇怪。一个人的名字由若干个字符组成,同样的,这些字符的全排列的结果中的每一个字符串,也都是这个人的名字。例如,如果一个人名字是 ACM,那么 AMC, CAM, MAC, MCA, 等也都是这个人的名字。在这个国家中,没有两个名字相同的人。

度熊想统计这个国家的人口数量,请帮助度熊设计一个程序,用来统计每一个人在之前被统计过多少次。

Input

这里包括一组测试数据,第一行包含一个正整数NN,接下来的NN 行代表了 NN 个名字。NN 不会超过100,000100,000,他们的名字不会超过40位.

Output

对于每输入的一个人名,输出一个整数,代表这个人之前被统计了多少次。

Sample Input
5
ACM
MAC
BBA
ACM
BAB
Sample Output
0
1
0
2
1
题解:

没啥好说的,每个字符串排序一下,通过map记录count。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

#define max(x, y) ((x) > (y) ? (x) : (y))
#define min(x, y) ((x) < (y) ? (x) : (y))

map names;

int main()
{
    //freopen("input.txt", "r", stdin);

    int N;
    cin >> N;
    
    for (int i = 0; i < N; ++i)
    {
        string name;
        cin >> name;

        sort(name.begin(), name.end());
        cout << names[name] << endl;
        ++names[name];
    }
    return 0;
}

Problem E

 
 Accepts: 87
 
 Submissions: 445
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

小度熊是一个尽职尽责的程序熊,每天产出数千行的代码,代码当中存在很多判断条件。度熊想让自己代码中的这些条件不存在交集。为了简化问题,一个条件可以是一个『简单条件』或者是一个『复合条件』,简单条件由『变量』、『比较符』和『运算数』组成,其中『变量』是用小写字符表示的一串字符,『运算数』仅为整数,『运算符』包括:<、>、<=、>=、==。分别代表:小于、大于、小于等于、大于等于和等于关系。简单条件的格式固定,左边为变量名,中间为操作符,右边为数字。若干个『简单条件』中间通过英文逗号组成一个『复合条件』,各『简单条件』之间是逻辑与的关系,例如:

简单条件: a > 100

复合条件: duxiong < 1000 , a > 100

Input

这里包括一组测试数据,第一行一个正整数 N(1 \leq N \leq 1000)N(1N1000),接下来 NN 行,每行一个条件,条件可能是一个『简单条件』或者是一个『复合条件』。其中『变量』不会超过30个字符,『运算数』的绝对值在10,000以内。测试数据中,不同变量的数量不会超过30个。其中『变量』、『比较符』和『运算数』 之前和之后都有可能出现若干空格字符。所有简单规则都是按照『变量』『比较符』『运算数』这样的顺序定义的,没有特例。

Output

对于第 ii 个条件,输出其与前 i-1i1个条件是否存在交集非空的情况。如果不存在交集非空的其他条件,输出一行字符串:『unique』。否则按照从小到大的顺序输出与其存在非空交集的条件的编号,编号之间用空格分隔,最后一个编号末尾不加空格。各条件从1-N1N编号。

Sample Input
4
a < 100
c > 99
b > 100 , b == 99 , c < 98
a < 1000, a >= 99
Sample Output
Copy
unique
1
unique
1 2
题解:

先说一下题目意思,不太好懂,就是每一行的每个变量表示一个集合,某个变量集合为空,那么说明整个语句集合都是空,否则各变量分别对应一个集合。另外,如果某一行没有某个变量,就认为这个变量范围是无穷,题目没有说清楚,不过第一行第二行能体现。

理解了题目意思,那么整个题目难度不算大,就是比较繁琐,算得上大模拟题吧。每行存多个集合,一个变量必须合并集合,为空就非法。字符串处理注意细节,思路清晰容易写。

#include 
#include 
#include 
#include 
using namespace std;

#define MAXNUM 10010

struct Gather
{
    string varName;
    int left, right;
    Gather()
    {
        left = - MAXNUM;
        right = MAXNUM;
    }
};

vector Gathers[1010];
bool bLegal[1010];

void SkipSpace(const char *&sz)
{
    while (*sz && (*sz == ' ' || *sz == ',')) ++sz;
}

bool GetMixedRange(int l1, int r1, int l2, int r2, int &l, int &r)
{
    l = max(l1, l2);
    r = min(r1, r2);

    return l1 <= l2;
}

void AddtoGathers(int t, Gather obj)
{
    for (int i = 0; i < Gathers[t].size(); ++i)
    {
        if (Gathers[t][i].varName == obj.varName)
        {
            // 合并变量
            int l, r;
            if (GetMixedRange(Gathers[t][i].left, Gathers[t][i].right, 
                obj.left, obj.right, l, r))
            {
                Gathers[t][i].left = l;
                Gathers[t][i].right = r;
            }
            else
            {
                bLegal[t] = false;
            }
            return;
        }
    }

    Gathers[t].push_back(obj);
}

bool JudgeFixed(const Gather &a, const Gather &b)
{
    if (a.varName != b.varName) return true;

    int l = max(a.left, b.left), r = min(a.right, b.right);
    return l <= r;
}

void HandleGather(string str, int t)
{
    const char *sz = str.c_str();
    while (*sz)
    {
        string varName, oper;
        int opNum = 0;

        SkipSpace(sz);
        // varName
        while (isalpha(*sz))
        {
            varName += *sz;
            ++sz;
        }
        SkipSpace(sz);
        // oper
        while (*sz != ' ' && !isdigit(*sz))
        {
            oper += *sz;
            ++sz;
        }
        SkipSpace(sz);
        // obNum
        while (*sz && isdigit(*sz))
        {
            opNum = opNum * 10 + *sz - '0';
            ++sz;
        }

        Gather obj;
        obj.varName = varName;
        if (oper == "<")
        {
            obj.right = opNum - 1;
        }
        else if (oper == ">")
        {
            obj.left = opNum + 1;
        }
        else if (oper == "<=")
        {
            obj.right = opNum;
        }
        else if (oper == ">=")
        {
            obj.left = opNum;
        }
        else if (oper == "==")
        {
            obj.left = obj.right = opNum;
        }

        AddtoGathers(t, obj);
        if (!bLegal[t])
        {
            printf("unique\n");
            return;
        }
    }


    // begin to judge
    vector result;
    for (int i = 0; i <= t - 1; ++i)
    {
        if (!bLegal[i]) continue;

        bool ok = true;
        for (int j = 0; j < Gathers[i].size(); ++j)
        {
            for (int k = 0; k < Gathers[t].size(); ++k)
            {
                if (!JudgeFixed(Gathers[i][j], Gathers[t][k]))
                {
                    ok = false;
                    break;
                }
            }
            if (!ok) break;
        }

        if (ok)
            result.push_back(i+1);
    }

    if (result.empty())
    {
        printf("unique\n");
    }
    else
    {
        printf("%d", result[0]);
        for (int i = 1; i < result.size(); ++i)
        {
            printf(" %d", result[i]);
        }
        printf("\n");
    }
}

int main()
{
    //freopen("input.txt", "r", stdin);

    int N;
    scanf("%d",&N);
    for (int i = 0; i < N; ++i)
    {
        bLegal[i] = true;
    }
    string str;
    getline(cin, str);
    for(int i = 0; i < N; ++i)
    {
        getline(cin, str);
        HandleGather(str, i);
    }
    return 0;
}

你可能感兴趣的:(算法题目)