2016百度之星资格赛题解

题目:Problem A

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5685

题意:对于一个字符串,定义一个哈希值 ∏是连乘,给一个字符串s,现有n个操作,每次操作询问下标 l 到 r 之间的子串的哈希值,前缀的思想,先预处理出0到每一位的哈希值gi,现在只涉及到g[r]和g[l-1],不过因为涉及到取模操作,所以不能直接除,应该找g[l-1]的逆元e,答案便是g[r]*e%9973。

 1 #include<stdio.h>
 2 char s[100010];
 3 int a[100010];
 4 int exGcd(int a,int b,int &x,int &y)
 5 {
 6   if(b==0)
 7   {
 8     x=1;
 9     y=0;
10     return a;
11   }
12   int r=exGcd(b,a%b,x,y);
13   int t=x;
14   x=y;
15   y=t-a/b*y;
16   return r;
17 }
18 int main()
19 {
20   int n;
21   while(scanf("%d",&n)!=EOF)
22   {
23     scanf("%s",s+1);
24     a[0]=1;
25     for(int i=1;s[i];i++)
26     {
27       a[i]=a[i-1]*(s[i]-28)%9973;
28     }
29     int x,y;
30     while(n--)
31     {
32       scanf("%d%d",&x,&y);
33       int l,r;
34       exGcd(a[x-1],9973,l,r);
35       l=(l%9973+9973)%9973;
36       printf("%d\n",a[y]*l%9973);
37     }
38   }
39   return 0;
40 }
AC代码

===========================================

 

题目:Problem B

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5686

题意:有一个只含有1的字符串,现在可以将任意相邻的1变为2,问可以形成多少种不同的字符串。比如111可以变为12,21,111。给你一个n表示1的数量,输出不同字符串的数量。

思路:dp的水题,斐波拉契数列,大数。可以想象11111...可以分成(2)111...与1(1111)...,现在有两种情况:

  第一个1动:前两个1变为2,后面的111...再变,这种情况的数量是dp[n-2];

  第一个1不动:这种情况的数量是dp[n-1];

注意:n很大,需要大数处理,只需要加法,而且时间限制也不是很紧,随便过。

 1 #include<stdio.h>
 2 #include<string.h>
 3 struct BigNum
 4 {
 5   int s[1000];
 6   int so;
 7   BigNum Add(BigNum b)
 8   {
 9     BigNum ret;
10     memset(ret.s,0,sizeof(ret.s));
11     ret.so=0;
12     int i;
13     for(i=0;i<so&&i<b.so;i++)
14     {
15       ret.s[i]+=s[i]+b.s[i];
16       if(ret.s[i]>=10)
17       {
18         ret.s[ret.so]-=10;
19         ret.s[++ret.so]=1;
20       }
21       else ret.so++;
22     }
23     for(;i<so;i++)
24     {
25       ret.s[i]=s[i]+ret.s[i];
26       if(ret.s[i]>=10)
27       {
28         ret.s[ret.so]-=10;
29         ret.s[++ret.so]=1;
30       }
31       else ret.so++;
32     }
33     for(;i<b.so;i++)
34     {
35       ret.s[i]=b.s[i]+ret.s[i];
36       if(ret.s[i]>=10)
37       {
38         ret.s[ret.so]-=10;
39         ret.s[++ret.so]=1;
40       }
41       else ret.so++;
42     }
43     if(ret.s[ret.so]!=0) ret.so++;
44     return ret;
45   }
46   void get()
47   {
48     for(int i=so-1;i>=0;i--)
49     {
50       printf("%d",s[i]);
51     }
52   }
53 };
54 BigNum dp[210];
55 int main()
56 {
57   dp[1].so=1;
58   dp[1].s[0]=1;
59   dp[2].so=1;
60   dp[2].s[0]=2;
61   for(int i=3;i<201;i++)
62   {
63     dp[i]=dp[i-1].Add(dp[i-2]);
64   }
65   int n;
66   while(scanf("%d",&n)!=EOF)
67   {
68     dp[n].get();
69     printf("\n");
70   }
71   return 0;
72 }
AC代码

===========================================

 

题目:Problem C

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5687

题意:有三种操作,一是 添加一个单词s ,二是 删除所有前缀是s的单词 ,三是 询问是否存在前缀为s的单词。

思路:字典树,添加单词时,每一个前缀的num++,也就是如果此时sa的num为10,就说明前缀为sa的单词共有10个。删除所有前缀为s的单词时直接把s对应的num置0,要注意三点:

  一:这样删除的话添加单词abcdefg时,如果遇到abc的num为0,说明没有以abc为前缀的单词,所以应该将0向下更新,也就是说如果之前存在过abce,也就是此时abce的num是1,我添加abcdef时应该把abce更新为0,之所以要现在才更新是为了节省时间,不然会超时,注意只需要更新一层就够。

  二:删除abcd,置abcd的num为0,要向上更新,都减去abcd的num值。

  三:删除abcd,如果abc的num为0,说明没有abcd的存在,不能再往下进行,因为下面的num值都是假的。

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<stdlib.h>
  4 #include<algorithm>
  5 using namespace std;
  6 struct Node
  7 {
  8   int num;
  9   Node *fa;
 10   Node *next[26];
 11 };
 12 Node root;
 13 
 14 void Init()
 15 {
 16   root.num=0;
 17   for(int i=0;i<26;i++)
 18   {
 19     root.next[i]=NULL;
 20   }
 21 }
 22 
 23 Node *create(Node *fa)
 24 {
 25   Node *s=(Node*)malloc(sizeof(Node));
 26   s->fa=fa;
 27   s->num=0;
 28   for(int i=0;i<26;i++)
 29   {
 30     s->next[i]=NULL;
 31   }
 32   return s;
 33 }
 34 
 35 void Add(char *s)
 36 {
 37   Node *p=&root;
 38   for(int i=0;s[i];i++)
 39   {
 40     int pos=s[i]-'a';
 41     if(p->next[pos]==NULL)
 42     {
 43       p->next[pos]=create(p);
 44     }
 45     p=p->next[pos];
 46     if(p->num==0)
 47     {
 48       for(int j=0;j<26;j++)
 49       {
 50         if(p->next[j]!=NULL)
 51         {
 52           p->next[j]->num=0;
 53         }
 54       }
 55     }
 56     p->num++;
 57   }
 58 }
 59 
 60 void del(char *s)
 61 {
 62   Node *p=&root;
 63   for(int i=0;s[i];i++)
 64   {
 65     int pos=s[i]-'a';
 66     if(p->next[pos]==NULL) return ;
 67     else p=p->next[pos];
 68     if(p->num==0) return ;
 69   }
 70   int tmp=p->num;
 71   while(p!=&root)
 72   {
 73     p->num-=tmp;
 74     p=p->fa;
 75   }
 76 }
 77 
 78 bool find(char *s)
 79 {
 80   Node *p=&root;
 81   for(int i=0;s[i];i++)
 82   {
 83     int pos=s[i]-'a';
 84     if(p->next[pos]==NULL) return false;
 85     else p=p->next[pos];
 86     if(p->num==0) return false;
 87   }
 88   return true;
 89 }
 90 
 91 int main()
 92 {
 93   int n;
 94   char s[41],s1[10];
 95   scanf("%d",&n);
 96   Init();
 97   while(n--)
 98   {
 99     scanf("%s%s",s1,s);
100     if(s1[0]=='i')
101     {
102       Add(s);
103     }
104     else if(s1[0]=='s')
105     {
106       if(find(s)) printf("Yes\n");
107       else printf("No\n");
108     }
109     else
110     {
111       del(s);
112     }
113   }
114   return 0;
115 }
AC代码

===========================================

 

题目:Problem D

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5688

题意:定义ACM和CAM相同,也就是说通过ACM这三个字母全排列出来的所有字符串都相同,也就是只要是由ACM组成的字符串都相同。现在有n个操作,添加字符串s,同时输出这个字符串前面添加过多少次。

思路:字典树,全排列都相同,那么可以先对字符串s进行排序,这样就只需要统计s出现的次数。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<algorithm>
 5 using namespace std;
 6 struct Node
 7 {
 8   int num;
 9   Node *next[26];
10 };
11 Node root;
12 void Init()
13 {
14   root.num=0;
15   for(int i=0;i<26;i++)
16     root.next[i]=NULL;
17 }
18 void Add(char *s)
19 {
20   Node *p=&root;
21   for(int j=0;s[j];j++)
22   {
23     int i=s[j]-'A';
24     if(p->next[i]==NULL)
25     {
26       Node *s=(Node *)malloc(sizeof(Node));
27       for(int i=0;i<26;i++)
28         s->next[i]=NULL;
29       s->num=0;
30       p->next[i]=s;
31     }
32     p=p->next[i];
33   }
34   p->num++;
35   printf("%d\n",p->num-1);
36 }
37 
38 int main()
39 {
40   int n;
41   char s[41];
42   scanf("%d",&n);
43   while(n--)
44   {
45     scanf("%s",s);
46     sort(s,s+strlen(s));
47     Add(s);
48   }
49   return 0;
50 }
AC代码

===========================================

 

题目:Problem E

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5689

题意:有n个集合,每个集合由一个字符串表示,字符串表示分为复合条件和简单条件,简单条件就是由 变量 + 比较符 + 数字组成,复合条件就是通过逗号将简单条件并在一起,比如a<100,a>20,最终a的区间就是(20,100),现在问前i-1条(每一条)和第i条的交集是否为空,如果都为空就输出unique,否则就从小到大输出有交集的集合的编号。判断两个集合是否有交集就是说是否有相同变量的区间有重叠,比如第一条是a<100,第二条是a>50,那么这两个集合由交集。

坑:如果第一条是a<100,第二条里没有出现a的限制,那么有交集。。。题目设定

坑:如果第一条是a<100,第二条是a<10,b<100,b==100,第二条是空集,因为不存在合法的b,所以两条集合也没有交集。。。

思路:记录每一条记录的每一个变量的左右区间就行,时间很松。完全可以1000*1000*30*log(30),就是对每一个集合遍历前面所有的集合,再遍历每一个变量,判断是否有交集,用map记录,不然比较变量名会花很多时间。(题目理清楚,写下去就很简单了)

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<map>
  4 #include<string>
  5 using namespace std;
  6 
  7 struct Node
  8 {
  9   char s[35];
 10   int l,r;
 11   bool flag;
 12 };
 13 
 14 Node a[1010][35];
 15 int ao[1010];
 16 bool ff[1010];
 17 map<string,int> m[1010];
 18 
 19 int mo;
 20 void solve(int pos,char *s)
 21 {
 22   for(int i=0;s[i];i++)
 23   {
 24     if(s[i]<='z'&&s[i]>='a')
 25     {
 26       char t[35];
 27       int j;
 28       for(j=i;s[j];j++)
 29       {
 30         if(s[j]<='z'&&s[j]>='a') t[j-i]=s[j];
 31         else break;
 32       }
 33       t[j-i]=0;
 34       int to;
 35       if(m[pos].find(t)==m[pos].end())
 36       {
 37         m[pos][t]=++mo;
 38         strcpy(a[pos][mo].s,t);
 39         a[pos][mo].l=-10000;
 40         a[pos][mo].r=10000;
 41         a[pos][mo].flag=1;
 42         to=mo;
 43         ao[pos]=mo;
 44       }
 45       else to=m[pos][t];
 46       int flag=0;
 47       for(;s[j];j++)
 48       {
 49         if(s[j]=='<'&&s[j+1]=='=') flag=2;
 50         else if(s[j]=='<') flag=1;
 51         else if(s[j]=='>'&&s[j+1]=='=') flag=4;
 52         else if(s[j]=='>') flag=3;
 53         else if(s[j]=='='&&s[j+1]=='=') flag=5;
 54         if(flag!=0) break;
 55       }
 56       int k=0,fu=1;
 57       for(j++;s[j];j++)
 58       {
 59         if(s[j]=='-')
 60         {
 61           fu=-1;
 62         }
 63         else if(s[j]<='9'&&s[j]>='0')
 64         {
 65           k=k*10+s[j]-'0';
 66         }
 67         else if(s[j]==',') break;
 68       }
 69       k=k*fu;
 70       if(flag==1) k--;
 71       else if(flag==3) k++;
 72       if(flag==1||flag==2)
 73       {
 74         a[pos][to].r=min(k,a[pos][to].r);
 75       }
 76       else if(flag==3||flag==4)
 77       {
 78         a[pos][to].l=max(k,a[pos][to].l);
 79       }
 80       else if(flag==5)
 81       {
 82         if(a[pos][to].l>k||a[pos][to].r<k) a[pos][to].flag=0;
 83         else
 84         {
 85           a[pos][to].l=k;
 86           a[pos][to].r=k;
 87         }
 88       }
 89       if(a[pos][to].l>a[pos][to].r) a[pos][to].flag=0;
 90       if(a[pos][to].flag==0) ff[pos]=1;
 91       i=j-1;
 92     }
 93   }
 94 }
 95 
 96 int main()
 97 {
 98   int n;
 99   char s[100];
100   while(scanf("%d",&n)!=EOF)
101   {
102     getchar();
103     memset(ff,0,sizeof(ff));
104     for(int i=0;i<n;i++)
105     {
106       mo=0;
107       gets(s);
108       solve(i,s);
109     }
110     for(int i=0;i<n;i++)
111     {
112       bool flag=0;
113       for(int u=0;u<i;u++)
114       {
115         if(ff[i]==1) continue;
116         if(ff[u]==1) continue;
117         int j;
118         for(j=1;j<=ao[i];j++)
119         {
120           if(a[i][j].flag==0) break;
121           int pos=m[u][a[i][j].s];
122           if(pos==0) break;
123           if(a[i][j].r<a[u][pos].l||a[i][j].l>a[u][pos].r);
124           else break;
125         }
126         if(j>ao[i]);
127         else
128         {
129           if(flag==1) printf(" ");
130           flag=1;
131           printf("%d",u+1);
132         }
133       }
134       if(flag==0) printf("unique\n");
135       else printf("\n");
136     }
137   }
138   return 0;
139 }
AC代码
posted @ 2016-05-16 21:43  hchlqlz  阅读(2257)  评论(0编辑  收藏  举报