題目: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 }
