比赛链接:

https://ac.nowcoder.com/acm/contest/33191

A.Array

题意:

给定一个长为 \(n\) 的序列 \(a\),满足 \(\sum_{i = 1}^{n} \frac{1}{a_i} <= \frac{1}{2}\),要求构造一个循环序列 \(b\),使得每 \(a_i\)\(i\) 都至少出现一次。

思路:

先转化一下不等式。
\(\sum_{i = 1}^{n} \frac{1}{a_i} <= \frac{1}{2}\)
= \(\sum_{i = 1}^{n} \frac{1}{\frac{a_i}{2}} <= 1\)
= \(\sum_{i = 1}^{n} \frac{1}{2^{\lfloor log_{2} a_i \rfloor}} <= \sum_{i = 1}^{n} \frac{1}{\frac{a_i}{2}} <= 1\)
所以可以将 \(a_i\) 先转化为 \(2^{\lfloor log_{2} a_i \rfloor}\),接着对于每 \(2^{\lfloor log_{2} a_i \rfloor}\) 位都要放入一个 \(i\),所以先考虑对 \(2^{\lfloor log_{2} a_i \rfloor}\) 排序,然后由小到大去构造这个序列。
每次一定能保证有位置放 \(i\),具体可以看题解证明。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e5 + 10;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n;
	cin >> n;
	vector <int> a(n + 1);
	vector < pair<int, int> > b(n + 1);
	for (int i = 1; i <= n; i ++ ){
		cin >> a[i];
		int x = 1;
		while(x * 2 <= a[i]){
			x *= 2;
		}
		b[i].first = x;
		b[i].second = i;
	}
	sort(b.begin() + 1, b.end());
	int pos = 1;
	vector <int> ans(N);
	for (int i = 1; i <= n; i ++ ){
		for (int j = pos; j <= N - 10; j += b[i].first)
			ans[j] = b[i].second;
		while(ans[pos]){
			pos ++ ;
		}
	}
	cout << N - 10 << "\n";
	for (int i = 1; i <= N - 10; i ++ ){
		if (ans[i]) cout << ans[i];
		else cout << 1;
		cout << " \n"[i == N - 10];
	}
	return 0;
}

B.Eezie and Pie

题意:

\(n\) 个节点的以 1 为根的树,每个节点可以向它的 0 到 \(d[i]\) 级祖先贡献 1 的价值,输出每个点的价值。

思路:

通过树上差分去求解,从根往下搜,记录路径,将每个节点对祖先的贡献通过差分数组记录,然后进行前缀和即可。

代码:

#include <bits/stdc++.h>
using namespace std;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n;
	cin >> n;
	vector < vector<int> > e(n + 1);
	for (int i = 0; i < n - 1; i ++ ){
		int u, v;
		cin >> u >> v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	vector <int> d(n + 1);
	for (int i = 1; i <= n; i ++ )
		cin >> d[i];
	vector <int> dep(n + 1), sum(n + 1), a(n + 1);
	function<void(int, int)> dfs = [&](int u, int fa){
		dep[u] = dep[fa] + 1;
		a[dep[u]] = u;
		sum[u] ++ ;
		sum[a[max(0, dep[u] - d[u] - 1)]] -- ;
		for (auto v : e[u]){
			if (v == fa) continue;
			dfs(v, u);
			sum[u] += sum[v];
		}
	};
	dfs(1, 0);
	for (int i = 1; i <= n; i ++ )
		cout << sum[i] << " \n"[i == n];
	return 0;
}

G.Icon Design

题意:

输出指定大小的图案。

思路:

按题意模拟即可。

代码:

#include <bits/stdc++.h>
using namespace std;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n;
	cin >> n;
	vector a(4 * n + 6, vector<char>(13 * n + 20, '.'));
	//*
	for (int j = 1; j <= 13 * n + 19; j ++ )
		a[1][j] = a[4 * n + 5][j] = '*';
	for (int i = 1; i <= 4 * n + 5; i ++ )
		a[i][1] = a[i][13 * n + 19] = '*';
	//N
	for (int i = n + 2; i <= 3 * n + 4; i ++ )
		a[i][n + 3] = a[i][3 * n + 5] = '@';
	for (int i = n + 2; i <= 3 * n + 4; i ++ )
		a[i][i + 1] = '@';
	//F
	for (int i = n + 2; i <= 3 * n + 4; i ++ )
		a[i][4 * n + 7] = '@';
	for (int j = 4 * n + 7; j <= 6 * n + 9; j ++ )
		a[n + 2][j] = a[2 * n + 3][j] = '@';
	//L
	for (int i = n + 2; i <= 3 * n + 4; i ++ )
		a[i][7 * n + 11] = '@';
	for (int j = 7 * n + 11; j <= 9 * n + 13; j ++ )
		a[3 * n + 4][j] = '@';
	//S
	for (int i = n + 2; i <= 2 * n + 3; i ++ )
		a[i][10 * n + 15] = '@';
	for (int i = 2 * n + 3; i <= 3 * n + 4; i ++ )
		a[i][12 * n + 17] = '@';
	for (int j = 10 * n + 15; j <= 12 * n + 17; j ++ )
		a[n + 2][j] = a[2 * n + 3][j] = a[3 * n + 4][j] = '@';
	//output
	for (int i = 1; i <= 4 * n + 5; i ++ ){
		for (int j = 1; j <= 13 * n + 19; j ++ )
			cout << a[i][j];
		cout << "\n";
	}
	return 0;
}

J.Number Game

题意:

有三个数 \(A, B, C\),每次可以让 \(B = A - B\)\(C = B - C\),问进行若干次操作后能否得到 \(x\)

思路:

某种操作连续进行两次后就等于没操作,所以两个操作一定是轮流进行的,那么答案只可能有两种。
\(C + k * (A - 2 * B)\)\(B - C + k * (A - 2 * B)\),要特判一下 \(A - 2 * B\) 的情况。

代码:

#include <bits/stdc++.h>
using namespace std;
void solve(){
	int A, B, C, x;
	cin >> A >> B >> C >> x;
	if (A == 2 * B){
		if (x == C || x == B - C) cout << "Yes\n";
		else cout << "No\n";
	}
	else{
		if ((x - C) % (A - 2 * B) == 0 || (x - B + C) % (A - 2 * B) == 0) cout << "Yes\n";
		else cout << "No\n";
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

M.Z-Game on grid

题意:

两人轮流在 \(n * m\) 的格子上移动棋子,刚开始棋子在 (1, 1),每次可以向某一维度的正方向移动,如果到达了标有 'A' 的格子,则先手胜,到达了标有 'B' 的格子,后手胜,如果没碰到任何有标志的格子,最后到到了 \((n, m)\),则平局。问在后手移动方向不确定的时候,先手能否保证先手胜、平局或后手胜。

思路:

如果反过来走的话,那么每次移动之后,没有后效性,所以用动态规划。
定义 \(dp[i][j]\) 表示在 \((i, j)\) 这个点的时候,能否保证一个结局。定义先手胜的情况为 1,平局为 4,后手胜为 2。
最后只需要判断 \(dp[1][1]\) & 上各自的情况是否大于 0,即可知道能否保证该种情况发生。
对于 \(dp[i][j]\),如果先手移动,只要有一个方向的结果符合自己的要求,就能保证这种情况的发生,得到 \(dp[i][j] = dp[i][j + 1]\) | \(dp[i + 1][j]\)。如果该后手移动了,只有当两个方向都是自己想要的结果,才能保证这种情况的发生,得到 \(dp[i][j] = dp[i][j + 1]\) & \(dp[i + 1][j]\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 5e2 + 10;
char a[N][N];
int dp[N][N];
void solve(){
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i ++ )
		for (int j = 1; j <= m; j ++ )
			cin >> a[i][j];
	for (int i = n; i >= 1; i -- ){
		for (int j = m; j >= 1; j -- ){
			if (a[i][j] == 'A') dp[i][j] = 1;
			else if (a[i][j] == 'B') dp[i][j] = 2;
			else if (i == n && j == m) dp[i][j] = 4;
			else if (i == n) dp[i][j] = dp[i][j + 1];
			else if (j == m) dp[i][j] = dp[i + 1][j];
			else if ((i + j) % 2 == 0) dp[i][j] = dp[i][j + 1] | dp[i + 1][j];
			else dp[i][j] = dp[i][j + 1] & dp[i + 1][j];
		}
	}
	array t{1, 4, 2};
	for (int i = 0; i < 3; i ++ ){
		if (dp[1][1] & t[i]) cout << "yes";
		else cout << "no";
		cout << " \n"[i == 2];
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}
posted on 2022-08-14 13:11  Hamine  阅读(21)  评论(0)    收藏  举报