好久没做CF了,前两天心血来潮做了一场Div2,找找手感...
Problem A. Helpful Maths
给一个只有加号和数字123的算式,将数字排序后输出。
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
char s[200];
while (~scanf("%s", s)) {
vector<char> vec;
for (int i = 0; s[i]; i++) {
if (s[i] == '+') continue;
vec.push_back(s[i]);
}
sort(vec.begin(), vec.end());
printf("%c", vec[0]);
for (int i = 1; i < vec.size(); i++)
printf("+%c", vec[i]);
puts("");
}
return 0;
}
Problem B. Xenia and Ringroad
有一个长度为n的环,编号分别为1...n。有m个任务{ai}需要完成,ai表示第i个任务要在编号为ai的点完成。
Xenia一开始在编号为1的点,他只能沿着环顺时针走,同时必须按顺序完成所有任务。问最少的步数。
一直统计当前编号到下一个任务编号的步数即可。
#include <cstdio>
#include <iostream>
using namespace std;
int n, m;
int main()
{
while (~scanf("%d%d", &n, &m)) {
long long ret = 0;
int pre = 1;
for (int i = 0; i < m; i++) {
int a; scanf("%d", &a);
if (a >= pre) {
ret += a - pre;
} else {
ret += n - (pre - a);
}
pre = a;
}
cout << ret << endl;
}
return 0;
}
Problem C. Xenia and Weights
给一个天平,初始两边为空。现在Xenia有一些砝码,问是否能进行满足下列条件的m次操作。
1. 每次放的砝码和上一次的不同。
2. 每次放砝码后都必须使得上一次较轻的一边称为较重的一边。
砝码的质量为1-10,m的范围是1-1000.
可以用dp[i][j][k] = 1表示第i次时,上一次放的砝码质量是j,两边差值为k的状态可达。因为差值可能为负数,加一个offset即可.
最后看dp[m][j][k], j = 1..10, k = 0..20是否有等于1的状态。如果有,则输出方案。
复杂度为O(m*10*20*10).
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1010;
int isok(int now, int delta) {
if (now <= 10 && now + delta > 10) return now + delta;
if (now > 10 && now - delta < 10) return now - delta;
return -1;
}
int dp[N][11][22];
int main()
{
char s[20]; int m;
while (~scanf("%s%d", s, &m)) {
vector<int> vec;
for (int i = 0; s[i]; i++) {
if (s[i] == '1') {
vec.push_back(i + 1);
}
}
memset(dp, 0, sizeof dp);
dp[0][0][10] = 1;
for (int i = 0; i < m; i++) {
for (int j = 0; j <= 10; j++) {
for (int k = 0; k <= 20; k++) {
for (auto &it: vec) {
if (it == j || !dp[i][j][k]) continue;
int next = isok(k, it);
if (~next && !dp[i+1][it][next]) {
dp[i+1][it][next] = 1;
}
}
}
}
}
int flag = 0, last; vector<int> ans;
for (int i = 1; i <= 10 && !flag; i++) {
for (int j = 0; j <= 20; j++) {
if (dp[m][i][j]) {
flag = 1;
last = j; ans.push_back(i);
break;
}
}
}
if (flag) {
for (int i = m; i > 1; i--) {
last += (1 - 2*(last > 10))*ans.back();
for (int j = 1; j <= 10; j++) {
if (dp[i-1][j][last] && j != ans.back()) {
ans.push_back(j);
break;
}
}
}
reverse(ans.begin(), ans.end());
puts("YES");
for (auto &it: ans) printf("%d ", it);
puts("");
} else {
puts("NO");
}
}
return 0;
}
Problem D. Xenia and Bit Operations
给一个长度为2^n的数组。每次交替进行下面两种操作得到一个长度是原来一半的数组:
1. a1 or a2, a3 or a4, ..., a_{2^n-1} or a_{2^n}.
2. a1 xor a2, a3 xor a4, ..., a_{2^n-1} xor a_{2^n}
当数组里只有一个数时停止。
现在有m个询问,每个询问的内容是改变原来数组中的一个数a_{p_i},问数组改变后操作得到的数是多少。
我们首先可以用原数组计算出每一次操作得到的数组,复杂度为O(2^n).
可以观察到,当改变原数组的一个数时,每一层的数组里只有一个数会受影响。
通过递推可以算出每一层受影响的数的值,复杂度为O(n),m次询问复杂度为O(m*n).
总的复杂度为O(2^n+m*n).
#include <cstdio>
#include <vector>
using namespace std;
int n, m;
vector<int> vec[20];
int main()
{
while (~scanf("%d%d", &n, &m)) {
for (int i = 0; i < 20; i++) vec[i].clear();
for (int i = 0; i < 1 << n; i++) {
int a; scanf("%d", &a);
vec[0].push_back(a);
}
for (int i = 1; i <= n; i++) {
for (int k = 0; k < vec[i-1].size(); k += 2) {
int now = (i % 2 ? vec[i-1][k]|vec[i-1][k+1] : vec[i-1][k]^vec[i-1][k+1]);
vec[i].push_back(now);
}
}
for (int i = 0; i < m; i++) {
int p, b; scanf("%d%d", &p, &b);
vec[0][--p] = b;
for (int i = 1; i <= n; i++) {
if (i % 2) {
vec[i][p/2] = vec[i-1][p]|vec[i-1][p^1];
} else {
vec[i][p/2] = vec[i-1][p]^vec[i-1][p^1];
}
p /= 2;
}
printf("%d\n", vec[n][0]);
}
}
return 0;
}
Problem E看上去有点奇葩,题解也写得有点奇怪,就没写...好像是比较优美的暴力...