Codeforces Round #197 (Div. 2)

好久没做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看上去有点奇葩,题解也写得有点奇怪,就没写...好像是比较优美的暴力...

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值