AtCoder Beginner Contest 222

A. 输出字符

给定一个长度为 l e n len len 的数字串,补全前缀零,使得输出串长度为 4 4 4

#include<bits/stdc++.h>
using namespace std;


int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	string s;
	cin >> s;
	for (int i = 0; i < 4 - (int) s.size(); i++) {
		cout << '0';
	}
	cout << s << endl;
	return 0;
}

B. 简单统计

给定数组 a a a 和数 p p p,统计 a a a 中有多少个数 < p <p <p

循环一边即可

复杂度: O ( N ) O(N) O(N)

#include<bits/stdc++.h>
using namespace std;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n, p, x, cnt = 0;
	cin >> n >> p;
	for (int i = 1; i <= n; i++) {
		cin >> x;
		cnt += x < p;
	}
	cout << cnt << endl;
	return 0;
}

C. 模拟

需要记录当前所有人的状态,即 ( x , p ) (x, p) (x,p) x x x 表示标号为 p p p 的人当前的分数

每次需要进行排序,以 x x x 降序,如果 x x x 相等,按 p p p 增序,模拟一遍过程即可

#include<bits/stdc++.h>
using namespace std;

string a[110];
int ans[110];

bool cmp(pair<int, int> a, pair<int, int> b) {
	if (a.first == b.first) return a.second < b.second;
	else return a.first > b.first;
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n, m;
	vector<pair<int, int>> vt;
	cin >> n >> m;
	for (int i = 0; i < n * 2; i++) {
		cin >> a[i];
		vt.push_back({0, i});
	}
	for (int i = 0; i < m; i++) {
		sort(vt.begin(), vt.end(), cmp);
		for (int j = 0; j < n; j++) {
			int p1 = vt[2 * j].second, p2 = vt[2 * j + 1].second;
			char cha = a[p1][i], chb = a[p2][i];
			if (cha == chb) {
				continue;
			}
			else {
				string v;
				v += cha; v += chb;
				if (v == "GC" || v == "CP" || v == "PG"){
					vt[2 * j].first++;
				}
				else {
					vt[2 * j + 1].first++;
				}
			}
		}
	}
	sort(vt.begin(), vt.end(), cmp);
	for (auto it: vt) {
		cout << it.second + 1 << endl;
	}
	return 0;
}

D. 动态规划

很容易容易想到一个 d p dp dp

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 i i i 个数,最后一个数取到的值为 j j j,所有满足条件的总情况数

那么有以下转移:

  • d p [ i ] [ j ] ← ∑ k = 1 j d p [ i − 1 ] [ k ] dp[i][j]\leftarrow \sum\limits_{k = 1}^{j}dp[i - 1][k] dp[i][j]k=1jdp[i1][k]

那么显然总复杂度是: O ( N M 2 ) O(NM^2) O(NM2),其中 M = max ⁡ A , B M = \max {A, B} M=maxA,B

这里 ∑ k = 1 j d p [ i − 1 ] [ k ] \sum\limits_{k = 1}^{j}dp[i - 1][k] k=1jdp[i1][k] 是前缀和的形式,我们把 d p [ i ] [ j ] dp[i][j] dp[i][j] 重新定义为前缀和的形式就可以 O ( 1 ) O(1) O(1) 转移了

那么转移变为 d p [ i ] [ j ] ← d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j] \leftarrow dp[i - 1][j] + dp[i][j - 1] dp[i][j]dp[i1][j]+dp[i][j1]

#include<bits/stdc++.h>
using namespace std;

const int N = 3e3 + 5, mod = 998244353;
int dp[N][N], a[N], b[N];

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++) {
		cin >> b[i];
	}
	fill(dp[0], dp[0] + N, 1);
	for (int i = 1; i <= n; i++) {
		for (int j = a[i]; j <= b[i]; j++) {
			dp[i][j] = (dp[i - 1][j] + dp[i][j - 1]) % mod;
		}
		for (int j = b[i] + 1; j < N; j++) {
			dp[i][j] = dp[i][j - 1];
		}
	}
	cout << dp[n][N - 1] << endl;
	return 0;
}

E. dfs 求路径 + dp

首先我们需要对于每条边,这条边被走过了多少次

我们可以 M M M 次 dfs, O ( N M ) O(NM) O(NM) 次的复杂度来求得

比如现在我们知道了每条边经过了多少次,计入 v v v 数组中, ∣ v ∣ = N − 1 |v| = N - 1 v=N1

那么每条边图成蓝色意味着权值为 + 1 +1 +1,否则权值为 − 1 -1 1

现在需要给这 N − 1 N - 1 N1 个数分配 + 1 +1 +1 − 1 -1 1 的权值,来使得和为 K K K,我们需要知道有多少种分配方式

每条路径的贡献是最多是 N − 1 N - 1 N1 那么 M M M 次贡献最多是 ( N − 1 ) M (N - 1)M (N1)M,那么有 ∑ v ≤ ( N − 1 ) M \sum\limits v \leq (N - 1)M v(N1)M

我们可以知道这 N − 1 N - 1 N1 个数通过加上 + 1 +1 +1 − 1 -1 1 的权值之后最大表示的范围是 [ − ( N − 1 ) M , ( N − 1 ) M ] [-(N - 1)M, (N - 1)M] [(N1)M,(N1)M]

那么我们可以 d p dp dp 来求所有可能的值以及情况数了

这个过程和 D D D 题很像

考虑到真实情况是非常稀疏的,所以可以考虑用 m a p map map 来转移(事实上确实跑得比较快)

#include<bits/stdc++.h>
using namespace std;

const int N = 1e3 + 5, mod = 998244353;
int n, m, k, fg;
vector<int> nxt[N];
int a[N];
map<pair<int, int>, int> cnt;
vector<int> path, ans;
map<int, int> pre, now;

void dfs(int now, int fa, int to) {
	path.push_back(now);
	if (now == to) {
		ans = path;
		return;
	}
	for (auto it: nxt[now]) {
		if (it == fa) continue;
		dfs(it, now, to);
	}
	path.pop_back();
}

void solve(int x, int y) {
	fg = 0;
	path.clear();
	ans.clear();
	dfs(x, -1, y);
	for (int i = 1; i < (int) ans.size(); i++) {
		int u = ans[i - 1], v = ans[i];
		cnt[{u, v}]++;
		cnt[{v, u}]++;
	}
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> n >> m >> k;
	for (int i = 1; i <= m; i++) {
		cin >> a[i];
	}
	for (int i = 1, u, v; i < n; i++) {
		cin >> u >> v;
		nxt[u].push_back(v);
		nxt[v].push_back(u);
	}
	for (int i = 1; i < m; i++) {
		solve(a[i], a[i + 1]);
	}
	vector<int> vv;
	for (int i = 1; i <= n; i++) {
		for (auto it: nxt[i]) {
			if (it > i) vv.push_back(cnt[{i, it}]);
		}
	}
	// for (auto it: vv) {
	// 	cout << it << " ";
	// }
	// cout << endl;
	now[0] = 1;
	for (auto val: vv) {
		pre = now;
		now.clear();
		for (auto it: pre) {
			now[it.first - val] += pre[it.first];
			now[it.first - val] %= mod;
			now[it.first + val] += pre[it.first];
			now[it.first + val] %= mod;
		}
	}
	cout << (now[k] + mod) % mod << endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值