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 #include2 #include 3 #include 4 #include 5 #include 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 }
B.Modulo Equality
题目大意:有两个长为n的数列a和b,现在让你给a中每个数加上一个数x并对m取模,并重新排列,使得两数列相同。求最小的x。
很明显x一定是在 (a1 - bi + m) mod m 中的一个数,我们只要枚举这个数,判断和数列b是否相同即可。
n最大只有2000,为了方便(我比较懒),判断相同时可以直接将a重新排序后判断,不会超时。
代码如下:
1 #include2 #include 3 #include 4 #include 5 #include 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 }
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 #include2 #include 3 #include 4 #include 5 #include 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 }
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 #include2 #include 3 #include 4 #include 5 #include 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 }
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 #include2 #include 3 #include 4 #include 5 #include 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 }