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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

猜你喜欢

转载自www.cnblogs.com/Dup4/p/10034250.html