Codeforces Round #609 (Div. 2)前五题题解

Codeforces Round #609 (Div. 2)前五题题解

补题补题……

C题写挂了好几个次,最后一题看了好久题解才懂……我太迟钝了……

然后因为longlong调了半个小时……

 

A.Equation

题目大意:有一个数字n,让你给出任意两个合数a,b满足a - b = n。

我们知道大于2的偶数都是合数,那么如果n为奇数,只要n加上一个奇数合数一定也为合数,如果n为偶数,那么n加上一个偶数合数也一定为合数。

因为只求任意一组,就直接选择4,9,若为奇数输出 9+n 和9,偶数输出 4+n 和4。

代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int main(){
26     int n;
27     scanf("%d", &n);
28     if(n % 2) printf("%d %d\n", n + 9, 9);
29     else printf("%d %d\n", n + 4, 4);
30     return 0;
31 }
View Code

 

B.Modulo Equality

题目大意:有两个长为n的数列a和b,现在让你给a中每个数加上一个数x并对m取,并重新排列,使得两数列相同。求最小的x。

很明显x一定是在 (a1 - bi + m) mod m 中的一个数,我们只要枚举这个数,判断和数列b是否相同即可。

n最大只有2000,为了方便(我比较懒),判断相同时可以直接将a重新排序后判断,不会超时。

代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN 2005
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int a[MAXN], b[MAXN], c[MAXN];
26 
27 int main(){
28     int n, m;
29     scanf("%d%d", &n, &m);
30     rep(i, 1, n) scanf("%d", &a[i]);
31     rep(i, 1, n) scanf("%d", &b[i]);
32     sort(a + 1, a + n + 1);
33     sort(b + 1, b + n + 1);
34     int ans = INF;
35     rep(i, 1, n){
36         int res = (b[1] - a[i] + m) % m;
37         rep(j, 1, n) c[j] = (a[j] + res) % m;
38         sort(c + 1, c + n + 1);
39         bool flag = 1;
40         rep(j, 1, n)
41             if(c[j] != b[j]){
42                 flag = 0;
43                 break;
44             }
45         if(flag) ans = min(ans, res);
46     }
47     printf("%d\n", ans);
48     return 0;
49 }
View Code

 

C.Long Beautiful Integer

题目大意:给你一个数字x,以及正整数k,求一个大于等于x的最小数字y并满足 yi = yi + k (1 <= i <= n - k) 。n为x的长度,n小于等于500.

这一题hack数据还真多,197组……

首先可以发现,数字y的长度一定是等于x的长度的,因为全为9的数字一定满足条件。

对于 yi = yi + k 这个式子,我们发现,对于所有位置i对k取模结果相同的,该位上的数字也一定是相同的。

那我们只要枚举最后前k位数字即可,为了让数字尽量小,我们让y的每一位和x一样大,然后判断此时y和x的大小。

若y大于等于x,那么y就是答案。若y小于x,只需要在第k位加上1即可。

注意:需要进位!!!

代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN 200005
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int num[MAXN], ans[MAXN];
26 char st[MAXN];
27 
28 int main(){
29     int n, m;
30     scanf("%d%d", &n, &m);
31     scanf("%s", st);
32     int maxx = 0;
33     rep(i, 1, n){
34         num[i] = st[i - 1] - '0';
35         maxx = max(maxx, num[i]);
36     }
37     int minx = INF, miny = INF;
38     rep(i, 1, m){
39         ans[i] = num[i];
40         for(int j = i ; j <= n; j += m){
41             if(ans[i] < num[j]) minx = min(minx, j);
42             if(ans[i] > num[j]) miny = min(miny, j);
43         }
44     }
45     if(miny > minx){
46         ans[m] = num[m] + 1;
47         for(int x = m; x > 0 && ans[x] == 10; x--){
48             ans[x] = 0;
49             ans[x - 1]++;
50         }
51     }
52     printf("%d\n", n);
53     rep(i, 1, n) printf("%d", ans[(i - 1) % m + 1]);
54     puts("");
55     return 0;
56 }
View Code

 

D.Domino for Young

题目大意:有一个不规则的网格长为n,第i列有 ai ,现在用1×2的骨牌覆盖它,求最多能放多少个骨牌。

这题是我人生第一道秒掉的D题!!!

一看题目,这不是《组合数学》第一章的例题嘛,虽然有点差别,但是思路基本一模一样。

先讲一下组合那道题,就是说有一个n×m的网格(n和m为偶数),将它的左上角和右下角两个格子去掉,证明用1×2的骨牌覆盖它,没有一种方法能将它完美覆盖。

这道题目就是0,1染色,对相邻的节点染上不同的颜色,如图所示。

 

每一个骨牌只能覆盖一个0和一个1,而这张图中一共有12个1和10个0,不合。

那么回到这一题,我们也可以将这个网格进行黑白染色,每一个骨牌也是只能覆盖一个0和一个1,那么在最优的覆盖方案中,覆盖完了0和1中较少的。答案就是0的个数和1的个数的最小值。

另外统计0,1个数就不多说了,详见代码。

代码如下:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN 200005
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int num[MAXN], ans[MAXN];
26 char st[MAXN];
27 
28 int main(){
29     int n, m;
30     scanf("%d%d", &n, &m);
31     scanf("%s", st);
32     int maxx = 0;
33     rep(i, 1, n){
34         num[i] = st[i - 1] - '0';
35         maxx = max(maxx, num[i]);
36     }
37     int minx = INF, miny = INF;
38     rep(i, 1, m){
39         ans[i] = num[i];
40         for(int j = i ; j <= n; j += m){
41             if(ans[i] < num[j]) minx = min(minx, j);
42             if(ans[i] > num[j]) miny = min(miny, j);
43         }
44     }
45     if(miny > minx){
46         ans[m] = num[m] + 1;
47         for(int x = m; x > 0 && ans[x] == 10; x--){
48             ans[x] = 0;
49             ans[x - 1]++;
50         }
51     }
52     printf("%d\n", n);
53     rep(i, 1, n) printf("%d", ans[(i - 1) % m + 1]);
54     puts("");
55     return 0;
56 }
View Code

 

 

E.K Integers

题目大意:有一数列p,长度为n。求最少的交换两个相邻数字的操作,使得数列p中存在连续子序列1,2,..., i 。其中i为1,2,..., n 。

这题大概一看就和逆序对有关,因为这个交换相邻操作和冒泡排序一模一样,而冒泡需要的操作就是逆序对的个数。

不难得出,当 i = n 时,答案就是逆序对个数。

对于其它的 i 来说,我们也可以得到这样一个贪心思想,先将1..i 放到一起,然后再将它们变成有序的。

变成有序的需要的操作很显然也可以按照 i = n 来做,但是将1..i 要放到哪里去呢?

根据初中学习的零点分段法,不知道的可以去看百度百科(传送门)了解一下。

最后将它们移到1..n在原数列中的中间一个位置(为了方便,后文用 mid 表示),一定是最优的,这可以用二分查找来实现。

至于它们需要移动多少呢?这要分成左右两段来算。

首先 mid 左边的全部移到中点,我们假设它们一共有 x 个,那么它们分别会被移到 mid - x , mid - x + 1 ,..., mid - 1 。需要移动的操作个数分别要减去它们在数列中的位置,总答案就是要减去它们的总和 sum ,这可以用线段树或树状数组维护。得出以下的式子。

ans = mid - x + mid - x + 1 + ... + mid - 1 - sum = mid  x - sum - x • (x + 1) / 2;

注意,在代码中为了方便x包含了在 mid 点上的点,所以变成了 mid • x - sum - x • (x - 1) / 2

另外在右边也是差不多,得出式子为 ans = sum - mid • x - x • (x + 1) / 2

代码中还有以下几个注意点:
1.其实当 i = n 的时候也是可以用上一个式子算的,后面两个值都为0,你可以手算一下。

2.需要两个树状数组,一个维护前缀和(当然也可以用归并排序并加一些记录,但是比较长),一个维护上述的 sum

3.要开longlong!要开longlong!!要开longlong!!!重要的事情说三遍。

代码如下:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <vector>
 6 #define rep(x, l, r) for(int x = l; x <= r; x++)
 7 #define repd(x, r, l) for(int x = r; x >= l; x--)
 8 #define clr(x, y) memset(x, y, sizeof(x))
 9 #define all(x) x.begin(), x.end()
10 #define pb push_back
11 #define mp make_pair
12 #define MAXN 200005
13 #define fi first
14 #define se second
15 #define SZ(x) ((int)x.size())
16 using namespace std;
17 typedef long long ll;
18 typedef vector<int> vi;
19 typedef pair<int, int> pii;
20 const int INF = 1 << 30;
21 const int p = 1000000009;
22 int lowbit(int x){ return x & (-x);}
23 int fast_power(int a, int b){ int x; for(x = 1; b; b >>= 1){ if(b & 1) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;}
24 
25 int n;
26 int a[MAXN], pos[MAXN];
27 ll tree0[MAXN], tree1[MAXN];
28 
29 void update0(int x, int y){
30     for(int i = x; i <= n; i += lowbit(i)) tree0[i] += y;
31 }
32 
33 void update1(int x, int y){
34     for(int i = x; i <= n; i += lowbit(i)) tree1[i] += y;
35 }
36 
37 ll query0(int x){
38     ll res = 0;
39     for(int i = x; i > 0; i -= lowbit(i)) res += tree0[i];
40     return res;
41 }
42 
43 ll query1(int x){
44     ll res = 0;
45     for(int i = x; i > 0; i -= lowbit(i)) res += tree1[i];
46     return res;
47 }
48 
49 int main(){
50     scanf("%d", &n);
51     rep(i, 1, n){
52         scanf("%d", &a[i]);
53         pos[a[i]] = i;
54     }
55     ll res0 = 0;
56     rep(i, 1, n){
57         res0 += i - 1 - query0(pos[i]);
58         update0(pos[i], 1);
59         update1(pos[i], pos[i]);
60         int l = 1, r = n, s;
61         while(l <= r){
62             int mid = (l + r) >> 1;
63             if(query0(mid - 1) * 2 <= i){
64                 s = mid;
65                 l = mid + 1;
66             }
67             else r = mid - 1;
68         }
69         ll left_sum0 = query0(s), left_sum1 = query1(s);
70         ll res1 = left_sum0 * s - left_sum1 - left_sum0 * (left_sum0 - 1) / 2;
71         ll right_sum0 = i - left_sum0, right_sum1 = query1(n) - left_sum1;
72         res1 += right_sum1 - right_sum0 * s - right_sum0 * (right_sum0 + 1) / 2;
73         printf("%lld ", res0 + res1);
74     }
75     puts("");
76     return 0;
77 }
View Code
posted @ 2020-01-10 20:00  Angel_Kanade  阅读(254)  评论(0编辑  收藏  举报