Codeforces Round #524 (Div. 2) Solution
A. Petya and Origami
Water.
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 ll n, k; 6 7 ll Get(ll x) 8 { 9 return (x * n) % k == 0 ? (x * n) / k : (x * n) / k + 1; 10 } 11 12 int main() 13 { 14 while (scanf("%lld%lld", &n, &k) != EOF) 15 { 16 ll res = Get(2) + Get(5) + Get(8); 17 printf("%lld\n", res); 18 } 19 return 0; 20 }
B. Margarite and the best present
Water.
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 int q, l, r; 6 ll get(ll x) 7 { 8 return x * ((x & 1) ? -1 : 1); 9 } 10 11 int main() 12 { 13 while (scanf("%d", &q) != EOF) 14 { 15 for (int qq = 1; qq <= q; ++qq) 16 { 17 scanf("%d%d", &l, &r); 18 if (l == r) printf("%lld\n", get(l)); 19 else 20 { 21 ll res = 0; 22 if ((l & 1) == 0) res = get(l++); 23 if (r & 1) res += get(r--); 24 res += ((r - l + 1) >> 1); 25 printf("%lld\n", res); 26 } 27 } 28 } 29 return 0; 30 }
C. Masha and two friends
Upsolved.
题意:
有一个黑白相间的棋盘,第一次选择一个矩形区域将区域内所有格子染白
第二次选择一个矩形区域将所有格子染黑,求最后白方块个数和黑方块个数
思路:
考虑先求整个棋盘的黑白方块个数,再删除两块矩形的黑白方块个数,矩形交部分要加一次
再求染色后,增加的黑块和白块个数
考虑怎么求黑白相间的黑白方块个数,发现如果矩形有一边长为偶数,那么两种颜色数量相同
否则,左下角是什么颜色,这个颜色的方块就多一个
其实,只求一种颜色就好了,因为总数是不变的
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define pll pair <ll, ll> 6 int t; ll x[4], y[4], X[2], Y[2], n, m, white, black; 7 pll tmp; 8 9 pll get(ll n, ll m, int vis) 10 { 11 ++n, ++m; 12 pll res = pll(0, 0); 13 if ((n & 1) && (m & 1)) 14 { 15 res.first = res.second = ((n - 1) * m) >> 1; 16 res.first += (m >> 1) + 1ll * (vis ^ 1); 17 res.second += (m >> 1) + 1ll * vis; 18 } 19 else 20 res.first = res.second = (n * m) >> 1; 21 return res; 22 } 23 24 ll get2(ll n, ll m) { ++n, ++m; return n * m; } 25 26 int main() 27 { 28 scanf("%d", &t); 29 while (t--) 30 { 31 scanf("%lld%lld", &n, &m); 32 for (int i = 0; i < 4; ++i) scanf("%lld%lld", x + i, y + i); 33 tmp = get(n - 1, m - 1, 0); 34 white = tmp.first, black = tmp.second; 35 tmp = get(y[1] - y[0], x[1] - x[0], (x[0] & 1) ^ (y[0] & 1)); 36 white -= tmp.first; black -= tmp.second; 37 tmp = get(y[3] - y[2], x[3] - x[2], (x[2] & 1)^ (y[2] & 1)); 38 white -= tmp.first, black -= tmp.second; 39 X[0] = max(x[0], x[2]); X[1] = min(x[1], x[3]); 40 Y[0] = max(y[0], y[2]); Y[1] = min(y[1], y[3]); 41 if (X[0] > X[1] || Y[0] > Y[1]) 42 { 43 white += get2(x[1] - x[0], y[1] - y[0]); 44 black += get2(x[3] - x[2], y[3] - y[2]); 45 } 46 else 47 { 48 tmp = get(Y[1] - Y[0], X[1] - X[0], (X[0] & 1) ^ (Y[0] & 1)); 49 white += tmp.first; 50 black += tmp.second; 51 white += get2(x[1] - x[0], y[1] - y[0]) - get2(X[1] - X[0], Y[1] - Y[0]); 52 black += get2(x[3] - x[2], y[3] - y[2]); 53 } 54 printf("%lld %lld\n", white, black); 55 } 56 return 0; 57 }
D. Olya and magical square
Upsolved.
题意:
给出一个$2^n \cdot 2^n 的矩形,每次可以画一个十字,使得被划区域的矩形分成四部分,新矩形边长减半$
$求画了K刀之后,是否存在左下角矩形到右上角矩形的一条通路,使得经过的矩形边长都相等$
$如果有,输出log2(边长)$
思路:
先考虑最多能画多少刀
注意到第一次可以画一刀,第二次可以画四刀,一共可以画N次,是一个等比数列
$\frac {4^n - 1}{3}$
暴力去逼近使得 $\frac {4^n - 1}{3} <= k$
这个时候对存在的现有矩形都多画一刀就要超过k了
那么我们假设已经画了$tot刀,我们需要考虑怎么分配剩下的k - tot 刀$
我们可以保持一个长度为曼哈顿距离的通路,那么这个通路以外的矩形随便画
或者这条通路上的矩形都要画一次
分类讨论一下是否满足即可
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 int t; ll n, k; 6 7 bool ok(ll k, ll n, ll tmp) 8 { 9 ll tot = 0; 10 if (k < 0) return false; 11 for (int i = 1; i <= n; ++i) 12 { 13 tot += tmp; 14 tmp *= 4; 15 if (tot >= k) return true; 16 } 17 return false; 18 } 19 20 int ok2(ll k, ll n, ll tmp) 21 { 22 ll tot = 0; 23 for (int i = 1; i <= n; ++i) 24 { 25 tot += tmp; 26 tmp *= 4; 27 if (tot == k) return i; 28 if (tot > k) return -1; 29 } 30 return -1; 31 } 32 33 int main() 34 { 35 scanf("%d", &t); 36 while (t--) 37 { 38 scanf("%lld%lld", &n, &k); 39 ll tot = 0, tmp = 1; int i; 40 for (i = 1; i <= n; ++i) 41 { 42 tot += tmp * tmp; 43 tmp <<= 1; 44 if (tot > k) 45 { 46 --i; 47 tmp >>= 1; 48 tot -= tmp * tmp; 49 break; 50 } 51 else if (tot == k) break; 52 } 53 if (i > n) puts("NO"); 54 else 55 { 56 if (i == n) printf("YES %d\n", 0); 57 else 58 { 59 //cerr << tot << " " << i << " " << tmp << endl; 60 int use; 61 if (ok(k - tot, n - i, tmp * tmp - tmp * 2 + 1)) printf("YES %lld\n", n - i); 62 else if (ok(k - tot - (tmp * 2 - 1), n - i, tmp * tmp - tmp * 2 + 1)) printf("YES %lld\n", n - i - 1); 63 else if ((use = ok2(k - tot, n - i, tmp * 2 - 1)) != -1) printf("YES %lld\n", n - i - use); 64 else puts("NO"); 65 } 66 } 67 } 68 return 0; 69 }
E. Sonya and Matrix Beauty
Upsolved.
题意:
有一个字符矩阵,对于一个子矩阵,对于每一行,可以任意改变字母顺序,使得每一行每一列都是回文串
那么这个子矩阵就是好矩阵,求有多少个子矩阵是好矩阵
思路:
先考虑一行,一行在改变顺序之后是回文串,那么其拥有奇数个字母的个数$<= 1$
再来考虑列,如果一个矩阵是好的矩阵
那么它的第一行中每种字母的个数和最后一行中要相同,第二行要和倒数第二行相同
注意到这和找回文串的思路很像
可以套用Manacher 算法的思路,$O(26n)处理$
再加上对于行的枚举
总的复杂度为$O(26n^3)$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 600 6 int n, m; 7 char s[N][N]; 8 int cnt[N][30]; 9 int Mp[N]; 10 11 bool okone(int x) 12 { 13 int res = 0; 14 for (int i = 0; i < 26; ++i) res += cnt[x][i] & 1; 15 return res <= 1; 16 } 17 18 bool ok(int x, int y) 19 { 20 for (int i = 0; i < 26; ++i) if (cnt[x][i] != cnt[y][i]) 21 return false; 22 return true; 23 } 24 25 int main() 26 { 27 while (scanf("%d%d", &n, &m) != EOF) 28 { 29 n <<= 1; 30 for (int i = 1; i <= n; i += 2) scanf("%s", s[i] + 1); 31 ll res = 0; 32 for (int l = 1; l <= m; ++l) 33 { 34 memset(cnt, 0, sizeof cnt); 35 for (int r = l; r <= m; ++r) 36 { 37 memset(Mp, 0, sizeof Mp); 38 for (int i = 1; i <= n; i += 2) ++cnt[i][s[i][r] - 'a']; 39 int Max = 0, pos = -1; 40 for (int i = 1; i <= n; ++i) 41 { 42 if (!okone(i)) 43 { 44 Mp[i] = 0; 45 continue; 46 } 47 if (Max > i) Mp[i] = min(Mp[2 * pos - i], Max - i); 48 else Mp[i] = 1; 49 while (i >= Mp[i] && i + Mp[i] <= n && okone(i + Mp[i]) && okone(i - Mp[i]) && ok(i + Mp[i], i - Mp[i])) 50 { 51 ++Mp[i]; 52 } 53 if (i + Mp[i] > Max) 54 { 55 Max = i + Mp[i]; 56 pos = i; 57 } 58 res += Mp[i] / 2; 59 } 60 } 61 } 62 printf("%lld\n", res); 63 } 64 return 0; 65 }
F. Katya and Segments Sets
Upsolved.
题意:
有n个集合,每个集合有若干个线段,每次询问一段连续的集合中,这些集合是否都有至少一条线段被$[l, r] 包含$
思路:
先考虑暴力,我们可以暴力枚举每个集合,所有$y <= r 中对应的x 的最大值是否 >= l$
然后考虑数据结构优化
先按r排序之后,再插入可持久化线段树之中。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 #define M 300010 6 #define INF 0x3f3f3f3f 7 int n, q, m, k, brr[M << 1]; 8 struct qnode 9 { 10 int l, r, p; 11 void scan() 12 { 13 scanf("%d%d%d", &l, &r, &p); 14 brr[++m] = r; 15 } 16 bool operator < (const qnode &other) const 17 { 18 return r < other.r; 19 } 20 }qarr[M]; 21 22 int GetHash(int x) { return lower_bound(brr + 1, brr + 1 + m, x) - brr; } 23 void Hash() 24 { 25 sort(brr + 1, brr + 1 + m); 26 m = unique(brr + 1, brr + 1 + m) - brr - 1; 27 for (int i = 1; i <= k; ++i) 28 qarr[i].r = GetHash(qarr[i].r); 29 } 30 31 namespace SEG 32 { 33 int T[M << 1], cnt; 34 struct node 35 { 36 int ls, rs, Min; 37 }a[M * 50]; 38 void build(int &now, int l, int r) 39 { 40 now = ++cnt; 41 a[now].Min = 0; 42 if (l == r) return; 43 int mid = (l + r) >> 1; 44 build(a[now].ls, l, mid); 45 build(a[now].rs, mid + 1, r); 46 } 47 void update(int &now, int pre, int l, int r, int pos, int val) 48 { 49 now = ++cnt; 50 a[now] = a[pre]; 51 if (l == r) 52 { 53 a[now].Min = max(a[now].Min, val); 54 return; 55 } 56 int mid = (l + r) >> 1; 57 if (pos <= mid) update(a[now].ls, a[pre].ls, l, mid, pos, val); 58 else update(a[now].rs, a[pre].rs, mid + 1, r, pos, val); 59 a[now].Min = min(a[a[now].ls].Min, a[a[now].rs].Min); 60 } 61 int query(int now, int l, int r, int ql, int qr) 62 { 63 if (l >= ql && r <= qr) return a[now].Min; 64 int mid = (l + r) >> 1; 65 int res = INF; 66 if (ql <= mid) res = min(res, query(a[now].ls, l, mid, ql, qr)); 67 if (qr > mid) res = min(res, query(a[now].rs, mid + 1, r, ql, qr)); 68 return res; 69 } 70 } 71 72 int main() 73 { 74 while (scanf("%d%d%d", &n, &q, &k) != EOF) 75 { 76 for (int i = 1; i <= k; ++i) qarr[i].scan(); Hash(); 77 sort(qarr + 1, qarr + 1 + k); 78 SEG::build(SEG::T[0], 1, n); 79 for (int i = 1; i <= k; ++i) 80 { 81 if (SEG::T[qarr[i].r] == 0) SEG::T[qarr[i].r] = SEG::T[qarr[i].r - 1]; 82 SEG::update(SEG::T[qarr[i].r], SEG::T[qarr[i].r], 1, n, qarr[i].p, qarr[i].l); 83 } 84 for (int qq = 1, a, b, x, y; qq <= q; ++qq) 85 { 86 scanf("%d%d%d%d", &a, &b, &x, &y); 87 y = upper_bound(brr + 1, brr + 1 + m, y) - brr - 1; 88 int tmp = SEG::query(SEG::T[y], 1, n, a, b); 89 if (tmp >= x) puts("yes"); 90 else puts("no"); 91 if (qq == q) return 0; 92 fflush(stdout); 93 } 94 } 95 return 0; 96 }