2016百度之星资格赛

Problem A

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

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

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

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

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

Input

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

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

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

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

Output

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

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

记hash(i)表示第一位到第i位不模9973的值,用一个前缀数组pre[i]表示前i位模9973的哈希值,那么第a-b位的哈希值hash(b)/hash(a-1),当然算的答案要模9973,所以pre[b]/pre[a-1]%9973既可,但是pre数组存放的值已经对9973取模了,所以要求一下乘法逆元然后乘上去取模就好。

代码:

#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <string>
#define fur(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
// (づ°ω°)づe★
inline int extend_gcd(int a,int b,int &x,int &y)
{
    if(a==0&&b==0)
    return -1;
    if(b==0)
    {        
        x=1;
        y=0;
        return a;
    }
    long long d=extend_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

inline int mod_reverse(int a,int n)     
{
    int x,y,d=extend_gcd(a,n,x,y);
    if(d==1)
        return (x%n+n)%n;
    else
        return -1;
}
int NY[9999];
int pre[100005];

int main(){
    fur(i,0,9973)NY[i] = mod_reverse(i,9973);
    int n;
    while(~scanf("%d",&n)){
        getchar();
        char s[100005];
        gets(s);
        int len = strlen(s);
        pre[0] = 1;
        int tmp = 1;
        fur(i,0,len-1){
            tmp = (tmp * (s[i] - 28))%9973;
            pre[i+1] = tmp;
        }
        int a,b;
        fur(i,1,n){
            scanf("%d %d",&a,&b);
            if(a>b){
                puts("0");
                continue;
            }
            else if(a == 1){
                int ans = pre[b];
                printf("%d\n",ans);
                continue;
            }
            int ans = pre[b];
            int ny = NY[pre[a-1]];
            ans = (ans * ny) % 9973;
            printf("%d\n",ans);
        }
    }
    return 0;
}


Problem B

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

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

Input

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

1≤N≤2001\leq N \leq 2001N200

Output

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

Sample Input
1
3
5
Sample Output
1
3
8


    
    
Hint
如果序列是:(111)。可以构造出如下三个新序列:(111), (21), (12)。
用dp[i]表示长度为i的序列能构造的新序列数,那么对于新增的第i个1,他要么不于前面的数合并,此时情况数为dp[i-1],要么与前面的1合并,此时的情况数为dp[i-2]

即dp[i] = dp[i-1] + dp[i-2]

i最大为200,要用下大数模板。数据有毒,0的时候直接输出空行。

代码:

#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cassert>
#define fur(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
// (づ°ω°)づe★
#define MAXN 9999
#define MAXSIZE 10
#define DLEN 4
class BigNum
{
private:
    int a[1500];    //可以控制大数的位数 
    int len;       //大数长度
public:
    BigNum() { len = 1; memset(a, 0, sizeof(a)); }   //构造函数
    BigNum(const int);       //将一个int类型的变量转化为大数
    BigNum(const char*);     //将一个字符串类型的变量转化为大数
    BigNum(const BigNum &);  //拷贝构造函数
    BigNum &operator=(const BigNum &);   //重载赋值运算符,大数之间进行赋值运算
    friend istream& operator>>(istream&, BigNum&);   //重载输入运算符
    friend ostream& operator<<(ostream&, BigNum&);   //重载输出运算符
    BigNum operator+(const BigNum &) const;   //重载加法运算符,两个大数之间的相加运算 
    BigNum operator-(const BigNum &) const;   //重载减法运算符,两个大数之间的相减运算 
    BigNum operator*(const BigNum &) const;   //重载乘法运算符,两个大数之间的相乘运算 
    BigNum operator/(const int   &) const;    //重载除法运算符,大数对一个整数进行相除运算
    BigNum operator^(const int  &) const;    //大数的n次方运算
    int    operator%(const int  &) const;    //大数对一个int类型的变量进行取模运算    
    bool   operator>(const BigNum & T)const;   //大数和另一个大数的大小比较
    bool   operator>(const int & t)const;      //大数和一个int类型的变量的大小比较
    void print();       //输出大数
    void printline();    //输出大数并换行 
};
BigNum::BigNum(const int b)     //将一个int类型的变量转化为大数
{
    int c, d = b;
    len = 0;
    memset(a, 0, sizeof(a));
    while (d > MAXN)
    {
        c = d - (d / (MAXN + 1)) * (MAXN + 1);
        d = d / (MAXN + 1);
        a[len++] = c;
    }
    a[len++] = d;
}
BigNum::BigNum(const char*s)     //将一个字符串类型的变量转化为大数
{
    int t, k, index, l, i;
    memset(a, 0, sizeof(a));
    l = strlen(s);
    len = l / DLEN;
    if (l%DLEN)
        len++;
    index = 0;
    for (i = l - 1; i >= 0; i -= DLEN)
    {
        t = 0;
        k = i - DLEN + 1;
        if (k<0)
            k = 0;
        for (int j = k; j <= i; j++)
            t = t * 10 + s[j] - '0';
        a[index++] = t;
    }
}
BigNum::BigNum(const BigNum & T) : len(T.len)  //拷贝构造函数
{
    int i;
    memset(a, 0, sizeof(a));
    for (i = 0; i < len; i++)
        a[i] = T.a[i];
}
BigNum & BigNum::operator=(const BigNum & n)   //重载赋值运算符,大数之间进行赋值运算
{
    int i;
    len = n.len;
    memset(a, 0, sizeof(a));
    for (i = 0; i < len; i++)
        a[i] = n.a[i];
    return *this;
}
istream& operator>>(istream & in, BigNum & b)   //重载输入运算符
{
    char ch[MAXSIZE * 4];
    int i = -1;
    in >> ch;
    int l = strlen(ch);
    int count = 0, sum = 0;
    for (i = l - 1; i >= 0;)
    {
        sum = 0;
        int t = 1;
        for (int j = 0; j<4 && i >= 0; j++, i--, t *= 10)
        {
            sum += (ch[i] - '0')*t;
        }
        b.a[count] = sum;
        count++;
    }
    b.len = count++;
    return in;
}
ostream& operator<<(ostream& out, BigNum& b)   //重载输出运算符
{
    int i;
    cout << b.a[b.len - 1];
    for (i = b.len - 2; i >= 0; i--)
    {
        cout.width(DLEN);
        cout.fill('0');
        cout << b.a[i];
    }
    return out;
}
BigNum BigNum::operator+(const BigNum & T) const   //两个大数之间的相加运算
{
    BigNum t(*this);
    int i, big;      //位数   
    big = T.len > len ? T.len : len;
    for (i = 0; i < big; i++)
    {
        t.a[i] += T.a[i];
        if (t.a[i] > MAXN)
        {
            t.a[i + 1]++;
            t.a[i] -= MAXN + 1;
        }
    }
    if (t.a[big] != 0)
        t.len = big + 1;
    else
        t.len = big;
    return t;
}
BigNum BigNum::operator-(const BigNum & T) const   //两个大数之间的相减运算 
{
    int i, j, big;
    bool flag;
    BigNum t1, t2;
    if (*this>T)
    {
        t1 = *this;
        t2 = T;
        flag = 0;
    }
    else
    {
        t1 = T;
        t2 = *this;
        flag = 1;
    }
    big = t1.len;
    for (i = 0; i < big; i++)
    {
        if (t1.a[i] < t2.a[i])
        {
            j = i + 1;
            while (t1.a[j] == 0)
                j++;
            t1.a[j--]--;
            while (j > i)
                t1.a[j--] += MAXN;
            t1.a[i] += MAXN + 1 - t2.a[i];
        }
        else
            t1.a[i] -= t2.a[i];
    }
    t1.len = big;
    while (t1.a[len - 1] == 0 && t1.len > 1)
    {
        t1.len--;
        big--;
    }
    if (flag)
        t1.a[big - 1] = 0 - t1.a[big - 1];
    return t1;
}
BigNum BigNum::operator*(const BigNum & T) const   //两个大数之间的相乘运算 
{
    BigNum ret;
    int i, j, up;
    int temp, temp1;
    for (i = 0; i < len; i++)
    {
        up = 0;
        for (j = 0; j < T.len; j++)
        {
            temp = a[i] * T.a[j] + ret.a[i + j] + up;
            if (temp > MAXN)
            {
                temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
                up = temp / (MAXN + 1);
                ret.a[i + j] = temp1;
            }
            else
            {
                up = 0;
                ret.a[i + j] = temp;
            }
        }
        if (up != 0)
            ret.a[i + j] = up;
    }
    ret.len = i + j;
    while (ret.a[ret.len - 1] == 0 && ret.len > 1)
        ret.len--;
    return ret;
}
BigNum BigNum::operator/(const int & b) const   //大数对一个整数进行相除运算
{
    BigNum ret;
    int i, down = 0;
    for (i = len - 1; i >= 0; i--)
    {
        ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
        down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
    }
    ret.len = len;
    while (ret.a[ret.len - 1] == 0 && ret.len > 1)
        ret.len--;
    return ret;
}
int BigNum::operator %(const int & b) const    //大数对一个int类型的变量进行取模运算    
{
    int i, d = 0;
    for (i = len - 1; i >= 0; i--)
    {
        d = ((d * (MAXN + 1)) % b + a[i]) % b;
    }
    return d;
}
BigNum BigNum::operator^(const int & n) const    //大数的n次方运算
{
    BigNum t, ret(1);
    int i;
    if (n<0)
        exit(-1);
    if (n == 0)
        return 1;
    if (n == 1)
        return *this;
    int m = n;
    while (m>1)
    {
        t = *this;
        for (i = 1; i << 1 <= m; i <<= 1)
        {
            t = t*t;
        }
        m -= i;
        ret = ret*t;
        if (m == 1)
            ret = ret*(*this);
    }
    return ret;
}
bool BigNum::operator>(const BigNum & T) const   //大数和另一个大数的大小比较
{
    int ln;
    if (len > T.len)
        return true;
    else if (len == T.len)
    {
        ln = len - 1;
        while (a[ln] == T.a[ln] && ln >= 0)
            ln--;
        if (ln >= 0 && a[ln] > T.a[ln])
            return true;
        else
            return false;
    }
    else
        return false;
}
bool BigNum::operator >(const int & t) const    //大数和一个int类型的变量的大小比较
{
    BigNum b(t);
    return *this>b;
}
void BigNum::print()    //输出大数
{
    int i;
    cout << a[len - 1];
    for (i = len - 2; i >= 0; i--)
    {
        cout.width(DLEN);
        cout.fill('0');
        cout << a[i];
    }
}
void BigNum::printline() {
    print();
    puts("");
}
BigNum numb[300];
int main(){
    //freopen("E:\\data.in","r",stdin);
    numb[0] = 1;
    numb[1] = 1;
    fur(i,2,250)numb[i] = numb[i-1] + numb[i-2];
    int n;
    while(cin>>n){
        if(n==0)puts("");
        //assert(n>0&&n<=200);
        else cout<<numb[n]<<endl;
    }
    return 0;
}


Problem C

 
 Accepts: 719
 
 Submissions: 5881
 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≤N≤100000)N (1\leq N\leq 100000)N(1N100000),代表度熊对于字典的操作次数,接下来NNN行,每行包含两个字符串,中间中用空格隔开。第一个字符串代表了相关的操作(包括: insert, delete 或者 search)。第二个字符串代表了相关操作后指定的那个字符串,第二个字符串的长度不会超过30。第二个字符串仅由小写字母组成。

Output

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

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

字典树的运用,每个结点表示该前缀出现的次数。要注意删除的时候要把前缀删干净,比如删除he的时候把he heh hehe hel hell hello删了,还要把h的次数减去2才行。

代码:

#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <string>
#define fur(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
// (づ°ω°)づe★
char op[10],word[50];
struct Node{
    int v;
    Node *next[26];
    Node(){
        v = 0;
        fur(i,0,25)next[i] = NULL;
    }
}root;

void Myinsert(){
    int len = strlen(word);
    Node *p = &root;
    fur(i,0,len-1){
        int id = word[i] - 'a';
        if(p->next[id] == NULL)    {
            p->next[id] = (Node*)malloc(sizeof(Node));
            fur(j,0,25) p->next[id]->next[j] = NULL;
            p->next[id]->v = 0;
        }
        p = p->next[id];
        p->v++;
    }
}

bool Mysearch(){
    int len = strlen(word);
    Node *p = &root;
    fur(i,0,len-1){
        int id = word[i] - 'a';
        if(p->next[id] == NULL || p->next[id]->v == 0)return false;
        p = p->next[id];
    }
    return true;
}

void freetree(Node *p){
    fur(i,0,25)if(p->next[i]!=NULL)freetree(p->next[i]);
    free(p);
}

void Mydelete(){
    int deleteValue,len = strlen(word);
    Node *p = &root;
    fur(i,0,len-1){
        int id = word[i] - 'a';
        if(p->next[id] == NULL||p->next[id]->v == 0)    
            return;
        p = p->next[id];
    }
    deleteValue = p->v;			//记录一下擦除的单词数量 
    p = &root;
    fur(i,0,len-1){
        int id = word[i] - 'a';
        p = p->next[id];
        p->v-=deleteValue;	//所有前缀节点都要减去被擦除的单词数 
    }
    fur(i,0,25)if(p->next[i]!=NULL){
        freetree(p->next[i]);
        p->next[i] = NULL;
    }
}

int main(){
    int T;
    cin>>T;
    while(T--){
        scanf("%s %s",op,word);
        if(op[0] == 'i') Myinsert();
        else if(op[0] == 'd') Mydelete();
        else if(Mysearch())puts("Yes");
        else puts("No");
    }
    return 0;
}

Problem D

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

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

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

Input

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

Output

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

Sample Input
5
ACM
MAC
BBA
ACM
BAB
Sample Output
0
1
0
2
1
把名字排序一下放到map里就好了..


#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <string>
#define fur(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
// (づ°ω°)づe★

map<string,int>m;
int main(){
    //freopen("E:\\data.in","r",stdin);
    int T;
    cin>>T;
    while(T--){
        char name[50];
        scanf("%s",name);
        int len = strlen(name);
        sort(name,name+len);
        string tmp = name;
        printf("%d\n",m[tmp]);
        m[tmp]++;
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值