A. Primal Sport
题意:有两个人轮流玩游戏。给出数X(i-1),轮到的人需要找到一个小于X(i-1)的素数x,然后得到Xi,Xi是x的倍数中大于等于X(i-1)的最小的数。现在已知X2,求最小的X0?
思路:根据题意,X1的取值范围为【X1-X2的最大质因子+1,X2),同理可知X0的取值范围为【X1-X1的最大质因子+1,,X1)。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 const int maxn = 1000010; 7 int Res[maxn],tmp[maxn]; 8 void Init() 9 { 10 memset(tmp, -1, sizeof(tmp)); 11 for (int i = 2; i < maxn; i++) 12 { 13 if (tmp[i] == -1) 14 { 15 for (int j = 2*i; j < maxn; j += i) tmp[j] = i;//标记质因子 16 } 17 if (tmp[i] == -1) Res[i] = i;//为质数 18 else Res[i] = i - tmp[i] + 1;//不是质数,tmp[i]为最大质因子 19 } 20 } 21 int main() 22 { 23 Init(); 24 int n; 25 scanf("%d", &n); 26 int L = Res[n]; 27 int ans = n; 28 for (int i = L; i < n; i++) ans = min(Res[i], ans); 29 printf("%d\n", ans); 30 return 0; 31 }
B. Producing Snow
题意:有n天,每天都会产生Vi体积的雪花堆(堆与堆之间独立),每天存在的雪花都会融化Ti,问n天中各天融化的体积和。
思路:用优先队列或最小堆维护,将Vi+sum(T1-Ti-1)加入队列或堆。堆中所有小于等于sum(T1-Ti)的都需要pop,当天融化的体积为所有pop出来的减去sum(Ti-Ti-1)加上还在堆里的数目*Ti.
堆实现:
1 #include<iostream> 2 #include<vector> 3 #include<algorithm> 4 #include<functional> 5 using namespace std; 6 const int maxn = 100010; 7 int V[maxn],T[maxn]; 8 vector<long long>Heap; 9 int main() 10 { 11 int n; 12 scanf("%d", &n); 13 make_heap(Heap.begin(), Heap.end(), greater<long long>());//建立最小堆 14 for (int i = 1; i <= n; i++) scanf("%d", V + i); 15 for (int i = 1; i <= n; i++) scanf("%d", T + i); 16 long long pre = 0; 17 for (int i = 1; i <= n; i++) 18 { 19 Heap.push_back(V[i]+pre); 20 push_heap(Heap.begin(), Heap.end(), greater<long long>()); 21 long long tot = 0; 22 while (Heap.size()&&Heap[0] <= T[i]+pre) 23 { 24 tot += Heap[0]-pre; 25 pop_heap(Heap.begin(), Heap.end(), greater<long long>()); 26 Heap.pop_back(); 27 } 28 if (Heap.size()) 29 { 30 tot += 1ll*T[i]*Heap.size(); 31 } 32 if (i == n) printf("%I64d\n", tot); 33 else printf("%I64d ", tot); 34 pre += T[i]; 35 } 36 return 0; 37 }
优先队列实现:
1 #include<queue> 2 #include<functional> 3 #include<iostream> 4 #include<cstdio> 5 using namespace std; 6 const int maxn = 100010; 7 int V[maxn], T[maxn]; 8 9 int main() 10 { 11 priority_queue<long long, vector<long long>, greater<long long> >pq; 12 int n; 13 scanf("%d", &n); 14 for (int i = 1; i <= n; i++) 15 { 16 scanf("%d", V + i); 17 } 18 for (int i = 1; i <= n; i++) 19 { 20 scanf("%d", T + i); 21 } 22 long long pre = 0; 23 for (int i = 1; i <= n; i++) 24 { 25 pq.push(V[i] + pre); 26 long long tot = 0; 27 while (pq.size() && pq.top() <= pre + T[i]) 28 { 29 tot += pq.top() - pre; 30 pq.pop(); 31 } 32 tot += pq.size()*T[i]; 33 pre += T[i]; 34 if (i == n) printf("%I64d\n", tot); 35 else printf("%I64d ",tot); 36 } 37 return 0; 38 }
C. Perfect Security
题意:有n个数字构成的序列A,有一串n个数的key——P,求出key的某一个重新排序的序列,使得Ai^Pi最后得到的结果串字典序最小,并输出该结果串。
思路:01异或字典树。注意插入的细节;查询时记得数目标记减1,因为P中每个数只能用一次。
1 #include<iostream> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdio> 5 using namespace std; 6 const int maxn = 300010; 7 int A[maxn],P[maxn]; 8 const int bits = 32; 9 const int SIZE = 2; 10 struct Trie 11 { 12 int Nums[maxn*bits][SIZE]; 13 int ex[maxn*bits*SIZE];//经过结点i的数的数目 14 int Tot; 15 16 void clear() 17 { 18 memset(Nums[0], 0, sizeof(Nums[0])); 19 Tot = 1; 20 } 21 22 void insert(int Num) 23 { 24 int Now = 0, curchr; 25 for (int i = 29; i >=0; i--) 26 { 27 int c = ((Num>>i)&1); 28 if (!Nums[Now][c]) 29 { 30 memset(Nums[Tot], 0, sizeof(Nums[Tot])); 31 ex[Tot] = 0; 32 Nums[Now][c] = Tot++; 33 } 34 ex[Nums[Now][c]]++;//表示经过该点的数的数目 35 Now = Nums[Now][c]; 36 } 37 } 38 int Search(int Num) 39 { 40 int u = 0,c; 41 for (int i = 29; i >=0; i--) 42 { 43 c =((Num>>i)&1); 44 if (ex[Nums[u][c]]) 45 { 46 u = Nums[u][c]; 47 ex[u]--; 48 Num ^= (c << i); 49 } 50 else 51 { 52 u = Nums[u][c^1]; 53 ex[u]--; 54 Num ^= ((c ^ 1) << i); 55 } 56 } 57 return Num; 58 } 59 60 }tree; 61 int main() 62 { 63 tree.clear(); 64 int n; 65 scanf("%d", &n); 66 for (int i = 1; i <= n; i++) 67 { 68 scanf("%d", A + i); 69 } 70 for (int i = 1; i <= n; i++) 71 { 72 scanf("%d", P+i); 73 } 74 for (int i = 1; i <= n; i++) tree.insert(P[i]); 75 for (int i = 1; i <= n; i++) 76 { 77 int ans = tree.Search(A[i]); 78 if (i == n) printf("%d\n", ans); 79 else printf("%d ", ans); 80 } 81 return 0; 82 }
D. Picking Strings
题意:给出S原串和T原串,每次各选取一个子区间,问在题目的变换下:A->BC,B->AC,C->AB,AAA->empty,是否能够从S串变换到T串?
思路:
B->AC->AAB->AAAC->C;C->AB->AAC->AAAB->B(B和C等价,可以将所有C替换为B)
AB->AAC->AAAB->B;B->AC->AB(B前面的A可以任意增减)
A->BC->BB(A可以转换为BB)
B->AB->BBB(已有B时,B的数量增加任意偶数个)
故只需考虑子串末尾A。由于末尾A无法被转化成,故原串和目标串末尾A数目需要保留相同个数
1.当T串B少,无解(B无法消除,只能增加)
2.当两串B数目奇偶数不同,无解(B只能增加偶数个)
3.T串末尾A比S串多,无解(末尾A无法增加,只能减少)
否则(两串B数目奇偶相同):
4.T串B多,S串末尾A多,必有解
5.两串B相同,S串末尾A比T串多的数目为3的倍数时有有解
6.两串A相同,S串有B即可。
7.其他无解
1 //B->AC->AAB->AAAC->C;C->AB->AAC->AAAB->B(B和C等价,可以将所有C替换为B) 2 //AB->AAC->AAAB->B;B->AC->AB(B前面的A可以任意增减) 3 //A->BC->BB(A可以转换为BB) 4 //B->AB->BBB(已有B时,B的数量增加任意偶数个) 5 //故只需考虑子串末尾A。由于末尾A无法被转化成,故原串和目标串末尾A数目需要保留相同个数 6 //1.当T串B少,无解(B无法消除,只能增加) 7 //2.当两串B数目奇偶数不同,无解(B只能增加偶数个) 8 //3.T串末尾A比S串多,无解(末尾A无法增加,只能减少) 9 //否则(两串B数目奇偶相同): 10 //4.T串B多,S串末尾A多,必有解 11 //5.两串B相同,S串末尾A比T串多的数目为3的倍数时有有解 12 //6.两串A相同,S串有B即可。 13 //7.其他无解 14 #include<iostream> 15 #include<algorithm> 16 #include<cstring> 17 #include<cstdio> 18 using namespace std; 19 const int maxn = 100010; 20 char S[maxn], T[maxn]; 21 //记录前i个字符中B+C的前缀和;记录以i结尾的连续A的个数 22 int Sa[maxn], Sb[maxn]; 23 int Ta[maxn], Tb[maxn]; 24 int main() 25 { 26 int n; 27 scanf("%s%s%d", S+1, T+1,&n); 28 int len1 = strlen(S+1), len2 = strlen(T+1); 29 for (int i = 1; i <= len1; i++) 30 { 31 if (S[i] == 'A') Sa[i] = Sa[i - 1] + 1; 32 Sb[i] = (S[i] == 'A') ? Sb[i - 1] : Sb[i - 1] + 1; 33 } 34 for (int i = 1; i <= len2; i++) 35 { 36 if (T[i] == 'A') Ta[i] = Ta[i - 1] + 1; 37 Tb[i] = (T[i] == 'A') ? Tb[i - 1] : Tb[i - 1] + 1; 38 } 39 while (n--) 40 { 41 int a, b, c, d; 42 scanf("%d%d%d%d", &a, &b, &c, &d); 43 int len1 = b - a + 1, len2 = d - c + 1; 44 int t_sa = min(len1, Sa[b]), t_sb = Sb[b] - Sb[a - 1]; 45 int t_ta = min(len2, Ta[d]), t_tb = Tb[d] - Tb[c - 1]; 46 //当T串B少,当两串B数目奇偶数不同,T串末尾A比S串多,无解 47 if (t_sb > t_tb || (t_sb % 2) != (t_tb % 2) || t_ta > t_sa) printf("0"); 48 else if(t_tb>t_sb&&t_sa>t_ta)//隐含两串B数目奇偶相同 49 {//T串B多,S串A多(多出的A可以转化为BB,前面非连续的A始终可以转化) 50 printf("1"); 51 } 52 else if (t_tb == t_sb) 53 {//此时只有Sa-Ta多出的a的数目为3的倍数才能转化 54 if ((t_sa - t_ta) % 3 == 0) printf("1"); 55 else printf("0"); 56 } 57 else if (t_ta == t_sa) 58 {//此时只要sb的数目大于0 59 if (t_sb) printf("1"); 60 else printf("0"); 61 } 62 else printf("0"); 63 } 64 printf("\n"); 65 return 0; 66 }
A2. Protect Sheep
题意:有一个牧场,‘S’为羊,现在需要放置‘D’狗,防止羊‘S’被狼‘W’通过上下左右吃到。任意一种方案即可,放置狗的数目不限
思路:简单DFS
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 const int maxn = 510; 6 char MP[maxn][maxn]; 7 bool Find[maxn][maxn]; 8 int dx[] = { 0,0,1,-1 }; 9 int dy[] = { 1,-1,0,0 }; 10 int R, C; 11 bool flag; 12 void DFS(int ux,int uy) 13 { 14 for (int i = 0; i < 4; i++) 15 { 16 if (!flag) return; 17 int tx = ux + dx[i], ty = uy + dy[i]; 18 if (tx >= R || tx < 0 || ty >= C || ty < 0) continue; 19 if (MP[tx][ty] == 'W') 20 { 21 flag = false; 22 return; 23 } 24 else if (MP[tx][ty] == 'S'&&!Find[tx][ty]) 25 { 26 Find[tx][ty] = true; 27 DFS(tx, ty); 28 } 29 else if (MP[tx][ty] == '.') MP[tx][ty] = 'D'; 30 } 31 } 32 int main() 33 { 34 scanf("%d%d", &R, &C); 35 flag = true; 36 for (int i = 0; i < R; i++) scanf("%s", MP + i); 37 for (int i = 0; i < R; i++) 38 { 39 for (int j = 0; j < C; j++) 40 { 41 if (MP[i][j] == 'S' && !Find[i][j]) 42 { 43 Find[i][j] = true; 44 DFS(i, j); 45 } 46 } 47 } 48 if (flag) 49 { 50 printf("Yes\n"); 51 for (int i = 0; i < R; i++) printf("%s\n", MP + i); 52 } 53 else printf("No\n"); 54 return 0; 55 }