1. 程式人生 > >Codeforces Round #524 (Div. 2) Solution

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