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 }
===========================================
题目: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 }
===========================================
链接: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 }
===========================================
题目: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 }
===========================================
题目: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 }