Codeforces Round #361 (Div. 2)
5/5
这场打得真的不太好,可以说是GG了,然而题解还是要补上~~
题A Mike and Cellphone
题意:给你一个数字键盘,然后给你划过的串,然后问你存不存在同样的手势但是不一样的串,也就是这个串表示的手势是否唯一?
题解:这道题其实可以枚举出所有可能的唯一的手势情况,然后直接判断。但是这样很费时间去推导。既然数据量小,那么我们就可以枚举所有的偏移情况,也就是给定一个移动的向量,看模拟移动之后是否存在一个合法的串,如果有就说明不唯一,否则就唯一。显然这个向量(x,y)的x和y是在[-3,3]之间。因此,这道题本质上可以说是模拟题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 #define lson l, m, rt*2
6 #define rson m + 1, r, rt*2+1
7 #define xx first
8 #define yy second
9
10 typedef pair<int,int> pii;
11 typedef long long ll;
12 typedef unsigned long long ull;
13
14 int main() {
15 // freopen("case.in", "r", stdin);
16 int n;
17 cin >> n;
18 string s;
19 cin >> s;
20 for (int i = -3; i <= 3; i++)
21 for (int j = -3; j <= 3; j++) if (i != 0 || j != 0) {
22 // cout << i << ' ' << j << endl;
23 bool ok = 1;
24 for (int k = 0; k < n; k++) {
25 int r, c;
26 if (s[k] == '0') r = 3, c = 1;
27 else r = (s[k] - '1') / 3, c = (s[k] - '1') % 3;
28 r += i;
29 c += j;
30 if (!((r == 3 && c == 1) || (r >= 0 && r <= 2 && c >= 0 && c <= 2))) ok = 0;
31 }
32 if (ok) { puts("NO"); return 0; }
33 }
34 puts("YES");
35 return 0;
36 }
题B Mike and Shortcuts
题意:给你1~n的门,然后可以花费|di - di+1|到达目的地,然后给你一个捷径,代表这个点可以花费一个能量到达,最后问你1到所有点的最短距离。
题解:这道题很简单,实际上就是一个最短路,每个点有三个门,左边、右边、捷径。然后就是类似spfa的找最短路。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 const int maxn = 2e5 + 10, inf = 1 << 30;
6 int d[maxn], t[maxn], inq[maxn], n;
7 queue<int> q;
8
9 void go_que(int u, int v) {
10 if (v < 1 || v > n) return;
11 if (d[v] > d[u] + 1) {
12 d[v] = d[u] + 1;
13 if (!inq[v]) {
14 q.push(v);
15 inq[v] = 1;
16 }
17 }
18 }
19
20 int main() {
21 // freopen("case.in", "r", stdin);
22 cin >> n;
23 for (int i = 1; i <= n; i++) {
24 scanf("%d", t + i);
25 d[i] = inf;
26 }
27 q.push(1);
28 inq[1] = 1;
29 d[1] = 0;
30 while (!q.empty()) {
31 int x = q.front(); q.pop();
32 inq[x] = 0;
33 go_que(x, t[x]);
34 go_que(x, x + 1);
35 go_que(x, x - 1);
36 }
37 for (int i = 1; i <= n; i++) {
38 printf("%d ", d[i]);
39 }
40 puts("");
41 }
题C Mike and Chocolate Thieves
题意:给定一个m,然后问你存不存m个满足的只有4个元素的序列,使得每个相邻元素之间的倍数是k(k > 1),如果存在输出最大的数n,否则输出-1?
题解:这个数n越大,显然个数越多,利用单调性,我们二分一个答案n,然后计算n有多少个?怎么计算呢?显然个数有(n / 8) + (n / 27) + (n / 64)……
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 typedef long long ll;
6 const int maxn = 1e5 + 100;
7
8
9 ll get_num(ll x) {
10 ll ret = 0;
11 for (int i = 2; ; i++) {
12 ll tmp = 1ll * i * i * i;
13 if (tmp > x) break;
14 ret += x / tmp;
15 }
16 return ret;
17 }
18
19 int main() {
20 // freopen("case.in", "r", stdin);
21 ll m;
22 cin >> m;
23 ll L = 7, R = 1e16;
24 while (R - L > 1) {
25 ll M = (L + R) / 2;
26 if (get_num(M) >= m) R = M; else L = M;
27 }
28 if (get_num(R) == m) cout << R << endl;
29 else cout << -1 << endl;
30 return 0;
31 }
题D Friends and Subsequences
题意:给你两个数组a和b,个数均为n个,然后让你数有多少对[l,r]使得a里面元素的最大值和b里面元素的最小值相等。
题解:这里我们观察到这个最大值-最小值是单调的,所以对于下标为i的起点,得到最大值-最小值的结果是:(负数) 0 0 0 (正数),所以我们利用二分搜索可以得到lower_bound和upper_bound,然后这个长度即为以i为起点的区间个数。利用rmq,复杂度可以做到O(nlogn)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 #define lson l, m, rt*2
6 #define rson m + 1, r, rt*2+1
7 #define xx first
8 #define yy second
9
10 typedef pair<int,int> pii;
11 typedef long long ll;
12 typedef unsigned long long ull;
13
14 const int maxn = 2e5 + 100, maxm = 30;
15 int v[2][maxn], dp[2][maxn][maxm];
16 int n;
17
18 void rmq_init() {
19 for (int i = 0; i < 2; i++)
20 for (int j = 0; j < n; j++) {
21 dp[i][j][0] = v[i][j];
22 }
23
24 for (int j = 1; (1 << j) <= n; j++) {
25 for (int i = 0; i + (1 << j) - 1 < n; i++) {
26 dp[0][i][j] = max(dp[0][i][j - 1], dp[0][i + (1 << (j - 1))][j - 1]);
27 dp[1][i][j] = min(dp[1][i][j - 1], dp[1][i + (1 << (j - 1))][j - 1]);
28 }
29 }
30 }
31
32 int query(int index, int L, int R) {
33 int k = 0;
34 while(1 << (k + 1) <= R - L + 1) k++;
35 if (index == 0) return max(dp[0][L][k], dp[0][R - (1 << k) + 1][k]);
36 else return min(dp[1][L][k], dp[1][R - (1 << k) + 1][k]);
37 }
38
39 int main() {
40 // freopen("case.in", "r", stdin);
41
42 cin >> n;
43 for (int i = 0; i < 2; i++)
44 for (int j = 0; j < n; j++) {
45 scanf("%d", &v[i][j]);
46 }
47 rmq_init();
48 ll res = 0;
49 for (int i = 0; i < n; i++) {
50 int L = i, R = n - 1;
51 int ansL = -1, ansR = -1;
52 while (L <= R) {
53 int M = (L + R) / 2;
54 int tmp = query(0, i, M) - query(1, i, M);
55 if (tmp >= 0) {
56 ansL = M; R = M - 1;
57 } else L = M + 1;
58 }
59 L = i, R = n - 1;
60 while (L <= R) {
61 int M = (L + R) / 2;
62 int tmp = query(0, i, M) - query(1, i, M);
63 if (tmp <= 0) {
64 ansR = M; L = M + 1;
65 } else R = M - 1;
66 }
67 if (ansL != -1 && ansR != -1) res += (ansR - ansL + 1);
68 }
69 cout << res << endl;
70
71 return 0;
72 }
题E Mike and Geometry Problem
题意:给你n个区间,任选k个区间,问你这k个区间相交的长度之和是多少?
题解:对于一段区间如果已知有x次相交,如果x >= k说明有C(x,k)个线段可以得到这个相交区间,所以先用map来离散表示区间,然后枚举每个区间,对于相交个数x,如果大于k的话,那么答案贡献就是C(x,k)*(区间的长度),这个做法类似于edu的一个题目,详见CF官方题解。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /*zhen hao*/
2 #include <bits/stdc++.h>
3 using namespace std;
4
5 #define lson l, m, rt*2
6 #define rson m + 1, r, rt*2+1
7 #define xx first
8 #define yy second
9
10 typedef pair<int,int> pii;
11 typedef long long ll;
12 typedef unsigned long long ull;
13
14 const int maxn = 2e5 + 100, mod = 1e9 + 7;
15 map<int,int> mp;
16 ll f[maxn], c[maxn];
17
18 ll quick(ll a, ll b, ll c) {
19 ll ret = 1;
20 while (b) {
21 if (b & 1) ret = ret * a % c;
22 b = b / 2;
23 a = a * a % mod;
24 }
25 return ret;
26 }
27
28 void init() {
29 f[0] = 1;
30 for (int i = 1; i < maxn; i++) f[i] = f[i - 1] * i % mod;
31 for (int i = 0; i < maxn; i++) c[i] = quick(f[i], mod - 2, mod);
32 }
33
34 ll C(int n, int k) {
35 if (n < k) return 0ll;
36 return (f[n] * c[k] % mod) * c[n - k] % mod;
37 }
38
39 int main() {
40 // freopen("case.in", "r", stdin);
41
42 init();
43 int n, k;
44 cin >> n >> k;
45 for (int i = 0; i < n; i++) {
46 int l, r;
47 scanf("%d%d", &l, &r);
48 mp[l]++;
49 mp[r + 1]--;
50 }
51 ll res = 0;
52 int last = mp.begin() -> first, sum = 0;
53 for (map<int,int>::iterator it = mp.begin(); it != mp.end(); it++) {
54 int x = it->first, y = it->second;
55 // cout << sum << endl;
56 // cout << x << ' ' << last << endl;
57 if (sum >= k) {
58 res += C(sum, k) * (x - last) % mod;
59 res %= mod;
60 }
61 last = x;
62 sum += y;
63 }
64 cout << res << endl;
65
66 return 0;
67 }