Educational Codeforces Round 81 (Rated for Div. 2)
A. Display The Number
题目链接:
http://codeforces.com/contest/1295/problem/A
题意:
一个显示屏,你可以显示很多数字通过亮起一堆段段闪瞎别人的dog眼,比如亮起一个数字1,需要亮起两段,亮起一个数字2,需要亮起5段(如题目图)
你最多可以亮起n段,问你可以显示最大的数字是多少。
分析:
用book[i]表示亮起数字i所需的段数,有: book[10]={6,2,5,5,4,5,6,3,7,6};
发现数字1需要的段最少,要让数字最大,首先他的位数要尽可能多,于是我们把尽量多的段去显示更多的1。
这时候如果n是偶数,就显示了 n/2 个1 ; 如果n是奇数,可以显示 n/2 个1 ,同时还剩下一个段,这时候可以把开头的1改成 7 ,book[ 1 ] = 2 ,book[ 7 ] = 3 , n就刚好用完。
这样就可以保证数字是最大的。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; #define ll long long #define ios std::ios::sync_with_stdio(false) const ll INF(0x3f3f3f3f3f3f3f3fll); const int inf(0x3f3f3f3f); ll book[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6}; int main() { ios; ll t; cin >> t; while (t--) { ll n; ///不超过n位的 cin >> n; if (n % 2 == 0) { for (int i = 0; i < n / 2; i++) { cout << 1; } } else { cout << 7; for (int i = 0; i < n / 2 - 1; i++) { cout << 1; } } cout << '\n'; } return 0; }
B. Infinite Prefixes
题目链接:
https://codeforces.com/contest/1295/problem/B
题意:
给你字符串长度n和x,然后给你长度为n的字符串s,s只包含0和1,字符串t是s的长度无限的重复串,比如s=01001,t是01001 01001....
然后问你 t 的某个前缀中 (0的数量 - 1的数量 ==x) 问这样的前缀有多少个。
分析:
满足条件的t的前缀,一定是由
①若干个的整个儿的字符串s(也可不包含)
②长度为 0~n的s的前缀
这两部分组成的,所以只要计算:
①字符串s中 cnt=0的数量 - 1的数量
②字符串s的前缀中 cnt_i[i]=前i个字符中,0的数量 - 1的数量
判断条件是 (x-cnt_i[i])%cnt == 0 即由若干个s和s的前i个字符组成的字符串满足条件 ans++;
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; #define ll long long #define ios std::ios::sync_with_stdio(false) const ll INF(0x3f3f3f3f3f3f3f3fll); const int inf(0x3f3f3f3f); int main() { ios; ll t; cin >> t; while (t--) { ll n, x; cin >> n >> x; string a; cin >> a; ll cnt_i[maxn]; ll cnt0 = 0, cnt1 = 0; for (ll i = 0; i < a.size(); i++) { ///计算前缀中cnt(0)-cnt(1),保存在cnt_i[i]中 if (a[i] == '0') cnt0++; else cnt1++; cnt_i[i] = cnt0 - cnt1; } ll cnt = cnt0 - cnt1; ///整个字符串s的cnt(0)-cnt(1) if (cnt == 0) { ///如果1和0的数量相当 if (x == 0) { cout << -1 << endl; continue; } else { int c = 0; for (int i = 0; i < a.size(); i++) { if (cnt_i[i] == x) { ///cnt*任意个+cnt_i[i]==x ,由于cnt==0,忽略 c++; } } if (c == 0) { cout << 0 << endl; continue; } else { ///如果存在cnt_i[i]==x 又cnt=0,无数个 cout << -1 << endl; continue; } } } else { int c = 0; if (x == 0) c++; ///空前缀的情况 for (int i = 0; i < a.size(); i++) if (((x - cnt_i[i]) % cnt == 0) && (x - cnt_i[i]) / cnt >= 0) c++; ///(x-cnt_i[i])/cnt>=0,因为cnt(0)-cnt(1)有正有负,所以要判断一下 cout << c << endl; } } return 0; }
C. Obtain The String
题目链接:
https://codeforces.com/contest/1295/problem/C
题意:
给你两个字符串s和t,有一个空串z,你可以疯狂的把s的任何子序列加到字符串z后面,使得z==t,问你最小操作次数。
分析:
就是问你怎么用s的子序列瞎鸡儿最有顺序的构造z。
首先,如果t中有一个字符,不存在于s中,当然不能构造啦,所以输出-1。
然后呢,我们遍历字符串z,当遍历到z[i]的时候,我们找s串里有没有z[i](肯定有啦,没有的都-1了),他的位置在哪里呢?设z[i]在s中的位置在pos(z[i])(说不定有很多个啦,先定义有一个好了)
①pos(z[i])>pos(z[i-1]) okk,z[i]选定完成啦!
②pos(z[i])<pos(z[i-1]) omg,选不了了,我上个字母选的位置 在 我现在想选的位置的后面啊 ,怎么办呢,结束当前子序列的添加,开始选下一个子序列,而下一个子序列的头 就是我现在想选的位置啦。
因为字符串都只包含小写字母,所以我们用vector[26]记录'a'~'z'每个字母,都存在s中的哪些位置。
然后用一个pre 记录,我上一次选的位置,now 记录,我这次想选的位置,如果now>pre,选定成功,反正,结束当前子序列,开始加下一个子序列。
因为每个字母会存在很多位置,所以用二分查找是否有大于pre的。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; #define ll long long #define ios std::ios::sync_with_stdio(false) const ll INF(0x3f3f3f3f3f3f3f3fll); const int inf(0x3f3f3f3f); int main() { ios; ll t; cin >> t; while (t--) { int ans = 0; string t, s; cin >> s >> t; vector<int> pos[26]; for (int i = 0; i < s.size(); i++) { ///记位置 pos[s[i] - 'a'].push_back(i); } bool ok = true; int pre = -1; for (int i = 0; i < t.size(); i++) { int temp = t[i] - 'a'; if (pos[temp].empty()) { ///如果没这个字母 ok = false; break; } int now = lower_bound(pos[temp].begin(), pos[temp].end(), pre) - pos[temp].begin(); if (now == pos[temp].size()) ///没找到比pre大的 { ans++; ///重新开始 pre = pos[temp][0] + 1; ///+1是为了避免连续字母找重复 } else { pre = pos[temp][now] + 1; } } if (!ok) { cout << -1 << '\n'; } else { cout << ans + 1 << '\n'; ///+1是因为最后的子序列的最后一个字符不会满足now==pos[temp].size()即ans少+1嘞 } } return 0; }
D. Same GCDs
题目链接:
https://codeforces.com/contest/1295/problem/D
题意:
给你a和b,让你计算有多少个x(0<=x<m)使得gcd(a,b)=gcd(a+x,b)
分析:
说实话这题咋一看完全没思路啊,后来被my candy,一个憨憨一个大佬提点才会做QAQ
是这样的首先,假设有两个数a,b,且gcd(a,b)=1,即a,b互质 , 那我要让gcd(a,b)=gcd(a+x,b)=1,只要找出有多少个数和b互质(数的范围在[0,b))对8?
没错, 找出和b互质的数有多少个 差不多这样就行了
可是gcd(a,b)不一定等于1呀 ?
但是gcd(a/gcd(a,b),b/gcd(a,b)) 一定等于 1 啊
于是乎,就变成了 找出有多少和 b/gcd(a,b) 互质的数(数的范围在[0,b/gcd(a,b))),而且这个数就是答案啦
就变成了求欧拉函数的问题了。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; #define ll long long #define ios std::ios::sync_with_stdio(false) const ll INF(0x3f3f3f3f3f3f3f3fll); const int inf(0x3f3f3f3f); long long gcd(long long b, long long c) //计算最大公约数 { return c == 0 ? b : gcd(c, b % c); } int main() { ios; ll t; cin >> t; while (t--) { ll a, m; cin >> a >> m; ll res = gcd(a, m); a /= res, m /= res; ll ans = m; for (ll i = 2; i * i <= m; i++) { if (m % i == 0) { ans -= ans / i; while (m % i == 0) m /= i; } } if (m > 1) ans -= ans / m; cout << ans << endl; } return 0; }