
Codeforces Round #609 (Div. 2)前五题题解
补题补题……
C题写挂了好几个次,最后一题看了好久题解才懂……我太迟钝了……
然后因为longlong调了半个小时……
A.Equation
题目大意:有一个数字n,让你给出任意两个合数a,b满足a - b = n。
我们知道大于2的偶数都是合数,那么如果n为奇数,只要n加上一个奇数合数一定也为合数,如果n为偶数,那么n加上一个偶数合数也一定为合数。
因为只求任意一组,就直接选择4,9,若为奇数输出 +n 和9,偶数输出 +n 和4。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = << ;
const int p = ;
int lowbit(int x){ return x & (-x);}
int fast_power(int a, int b){ int x; for(x = ; b; b >>= ){ if(b & ) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;} int main(){
int n;
scanf("%d", &n);
if(n % ) printf("%d %d\n", n + , );
else printf("%d %d\n", n + , );
return ;
}
B.Modulo Equality
题目大意:有两个长为n的数列a和b,现在让你给a中每个数加上一个数x并对m取模,并重新排列,使得两数列相同。求最小的x。
很明显x一定是在 (a1 - bi + m) mod m 中的一个数,我们只要枚举这个数,判断和数列b是否相同即可。
n最大只有2000,为了方便(我比较懒),判断相同时可以直接将a重新排序后判断,不会超时。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 2005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = << ;
const int p = ;
int lowbit(int x){ return x & (-x);}
int fast_power(int a, int b){ int x; for(x = ; b; b >>= ){ if(b & ) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;} int a[MAXN], b[MAXN], c[MAXN]; int main(){
int n, m;
scanf("%d%d", &n, &m);
rep(i, , n) scanf("%d", &a[i]);
rep(i, , n) scanf("%d", &b[i]);
sort(a + , a + n + );
sort(b + , b + n + );
int ans = INF;
rep(i, , n){
int res = (b[] - a[i] + m) % m;
rep(j, , n) c[j] = (a[j] + res) % m;
sort(c + , c + n + );
bool flag = ;
rep(j, , n)
if(c[j] != b[j]){
flag = ;
break;
}
if(flag) ans = min(ans, res);
}
printf("%d\n", ans);
return ;
}
C.Long Beautiful Integer
题目大意:给你一个数字x,以及正整数k,求一个大于等于x的最小数字y并满足 yi = yi + k ( <= 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即可。
注意:需要进位!!!
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 200005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = << ;
const int p = ;
int lowbit(int x){ return x & (-x);}
int fast_power(int a, int b){ int x; for(x = ; b; b >>= ){ if(b & ) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;} int num[MAXN], ans[MAXN];
char st[MAXN]; int main(){
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", st);
int maxx = ;
rep(i, , n){
num[i] = st[i - ] - '';
maxx = max(maxx, num[i]);
}
int minx = INF, miny = INF;
rep(i, , m){
ans[i] = num[i];
for(int j = i ; j <= n; j += m){
if(ans[i] < num[j]) minx = min(minx, j);
if(ans[i] > num[j]) miny = min(miny, j);
}
}
if(miny > minx){
ans[m] = num[m] + ;
for(int x = m; x > && ans[x] == ; x--){
ans[x] = ;
ans[x - ]++;
}
}
printf("%d\n", n);
rep(i, , n) printf("%d", ans[(i - ) % m + ]);
puts("");
return ;
}
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个数就不多说了,详见代码。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 200005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = << ;
const int p = ;
int lowbit(int x){ return x & (-x);}
int fast_power(int a, int b){ int x; for(x = ; b; b >>= ){ if(b & ) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;} int num[MAXN], ans[MAXN];
char st[MAXN]; int main(){
int n, m;
scanf("%d%d", &n, &m);
scanf("%s", st);
int maxx = ;
rep(i, , n){
num[i] = st[i - ] - '';
maxx = max(maxx, num[i]);
}
int minx = INF, miny = INF;
rep(i, , m){
ans[i] = num[i];
for(int j = i ; j <= n; j += m){
if(ans[i] < num[j]) minx = min(minx, j);
if(ans[i] > num[j]) miny = min(miny, j);
}
}
if(miny > minx){
ans[m] = num[m] + ;
for(int x = m; x > && ans[x] == ; x--){
ans[x] = ;
ans[x - ]++;
}
}
printf("%d\n", n);
rep(i, , n) printf("%d", ans[(i - ) % m + ]);
puts("");
return ;
}
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 + ,..., mid - 。需要移动的操作个数分别要减去它们在数列中的位置,总答案就是要减去它们的总和 sum ,这可以用线段树或树状数组维护。得出以下的式子。
ans = mid - x + mid - x + 1 + ... + mid - 1 - sum = mid • x - sum - x • (x + 1) / 2;
注意,在代码中为了方便x包含了在 mid 点上的点,所以变成了 mid • x - sum - x • (x - ) / 。
另外在右边也是差不多,得出式子为 ans = sum - mid • x - x • (x + ) / 。
代码中还有以下几个注意点:
1.其实当 i = n 的时候也是可以用上一个式子算的,后面两个值都为0,你可以手算一下。
2.需要两个树状数组,一个维护前缀和(当然也可以用归并排序并加一些记录,但是比较长),一个维护上述的 sum 。
3.要开longlong!要开longlong!!要开longlong!!!重要的事情说三遍。
代码如下:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define rep(x, l, r) for(int x = l; x <= r; x++)
#define repd(x, r, l) for(int x = r; x >= l; x--)
#define clr(x, y) memset(x, y, sizeof(x))
#define all(x) x.begin(), x.end()
#define pb push_back
#define mp make_pair
#define MAXN 200005
#define fi first
#define se second
#define SZ(x) ((int)x.size())
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;
const int INF = << ;
const int p = ;
int lowbit(int x){ return x & (-x);}
int fast_power(int a, int b){ int x; for(x = ; b; b >>= ){ if(b & ) x = 1ll * x * a % p; a = 1ll * a * a % p;} return x % p;} int n;
int a[MAXN], pos[MAXN];
ll tree0[MAXN], tree1[MAXN]; void update0(int x, int y){
for(int i = x; i <= n; i += lowbit(i)) tree0[i] += y;
} void update1(int x, int y){
for(int i = x; i <= n; i += lowbit(i)) tree1[i] += y;
} ll query0(int x){
ll res = ;
for(int i = x; i > ; i -= lowbit(i)) res += tree0[i];
return res;
} ll query1(int x){
ll res = ;
for(int i = x; i > ; i -= lowbit(i)) res += tree1[i];
return res;
} int main(){
scanf("%d", &n);
rep(i, , n){
scanf("%d", &a[i]);
pos[a[i]] = i;
}
ll res0 = ;
rep(i, , n){
res0 += i - - query0(pos[i]);
update0(pos[i], );
update1(pos[i], pos[i]);
int l = , r = n, s;
while(l <= r){
int mid = (l + r) >> ;
if(query0(mid - ) * <= i){
s = mid;
l = mid + ;
}
else r = mid - ;
}
ll left_sum0 = query0(s), left_sum1 = query1(s);
ll res1 = left_sum0 * s - left_sum1 - left_sum0 * (left_sum0 - ) / ;
ll right_sum0 = i - left_sum0, right_sum1 = query1(n) - left_sum1;
res1 += right_sum1 - right_sum0 * s - right_sum0 * (right_sum0 + ) / ;
printf("%lld ", res0 + res1);
}
puts("");
return ;
}