2023CCPC女生赛题解(K,A,L,F,G,H)

因为不会算法痛失Au :P

比去年进步了一点,模拟都写出来了而且一遍过。

“我要漂亮姐姐教我写代码”队,名字略抽象,师妹起的xD

补题链接:Dashboard - 2023年中国大学生程序设计竞赛女生专场 - Codeforces

K. RSP(思维,博弈)

思路

面对样例编程(bushi

简单的想就是,其实最后的结果取决于第一局的结果,如果第一局胜利,则只要后面的全部平局即可胜利;若第一局输了,那后面的也只能是平局使得结果不会更差,所以只需要计算第一局胜利的概率即可,即1/n。

Code

#include <bits/stdc++.h>

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int _ = 1;
	// std::cin >> _;
	while(_ --) {
		int n, m;
		std::cin >> n >> m;
		std::cout << 1 << '/' << n << '\n'; 
	}
	return 0;
}

A. 疾羽的救赎(模拟)

思路

模拟,注意每次移动都是连同在当前被移动的棋子上面的棋子一起移动的。

Code

#include <bits/stdc++.h>

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int _ = 1;
	std::cin >> _;
	while(_ --) {
		std::vector<int> v[10];
		v[2].push_back(1);
		v[3].push_back(2);
		v[4].push_back(3);
		int mp[] = {0, 2, 3, 4};
		mp[1] = 2, mp[2] = 3, mp[3] = 4;
		for(int i = 1; i <= 12; i ++) {
			int a, b, pos;
			std::cin >> a >> b;
			int now = mp[a];
			int nex = now + b;
			for(int j = 0; j < v[now].size(); j ++) {
				if(v[now][j] == a) {
					pos = j;
					break;
				}
			}
			for(int j = pos; j < v[now].size(); j ++) {
				mp[v[now][j]] = nex;
				v[nex].push_back(v[now][j]);
			}
			int len = v[now].size();
			for(int j = pos; j < len; j ++)
				v[now].pop_back();
		}
		std::cout << (v[9].size() == 3 ? "Y" : "N") << '\n';
	}
	return 0;
}

L. 养成游戏(DFS)

思路

DFS枚举每种情况,分别计算取最大值即可,时间复杂度O(m*9^6),大概5e7,可以过。

Code

#include <bits/stdc++.h>

typedef long long ll;
const int N = 105;
int a[15];

struct node {
	int i, j, op;
	ll a, b, d, v;
} e[N];

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int _ = 1, n, m, k;
	ll ans = 0;
	// std::cin >> _;

	std::function<void(int)> DFS = [&](int u) {
		if(u == n + 1) {
			ll res = 0;
			for(int i = 1; i <= m; i ++) {
				if(!e[i].op)
					res += (e[i].a * a[e[i].i] + e[i].b * a[e[i].j] <= e[i].d ? e[i].v : 0);
				else
					res += (e[i].a * a[e[i].i] + e[i].b * a[e[i].j] >= e[i].d ? e[i].v : 0);
			}
			ans = std::max(ans, res);
			return;
		}
		for(int i = 0; i <= k; i ++) {
			a[u] = i;
			DFS(u + 1);
		}
	};

	while(_ --) {
		std::cin >> n >> m >> k;
		for(int l = 1; l <= m; l ++) {
			int i, j, op, a, b, d, v;
			std::cin >> i >> j >> op >> a >> b >> d >> v;
			e[l] = {i, j, op, a, b, d, v};
		}
		DFS(1);
		std::cout << ans << '\n';
	}
	return 0;
}

F. 最长上升子序列(思维,LIS)

思路

从1开始按位置倒着赋值,从LIS长度递增的顺序赋值,例如样例:1 2 2 3 3,那就赋值为1 3 2 5 4,模拟这个赋值过程即可。

Code

#include <bits/stdc++.h>

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int _ = 1;
	// std::cin >> _;
	while(_ --) {
		int n, max = 0;
		std::cin >> n;
		std::vector<int> a(n + 1), cnt[n + 1], ans(n + 1);
		bool flag = true;
		for(int i = 1; i <= n; i ++) {
			std::cin >> a[i];
			if(a[i] > max + 1) 
				flag = false;
			else 
				max = std::max(max, a[i]);
			cnt[a[i]].push_back(i);
		}
		if(!flag) {
			std::cout << -1 << '\n';
			continue;
		}
		int num = 1;
		for(int i = 1; i <= n; i ++) {
			for(int j = cnt[i].size() - 1; j >= 0; j --) {
				ans[cnt[i][j]] = num ++;
			}
			if(cnt[i].empty()) break;
		}
		for(int i = 1; i <= n; i ++)
			std::cout << ans[i] << " \n"[i == n];
	}
	return 0;
}

G. 精灵宝可梦对战(模拟,队列)

思路

没那么大的模拟,我是用的队列模拟,每次模拟一回合的对战。

Code

#include <bits/stdc++.h>

const int N = 1e5 + 5;
int h1[N], a1[N], b1[N], c1[N], d1[N], e1[N], w1[N], ea[N];
int h2[N], a2[N], b2[N], c2[N], d2[N], e2[N], w2[N], eb[N];

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int n, m, k;
	std::cin >> n >> m >> k;
	std::queue<int> A, B;
	for(int i = 1; i <= n; i ++) {
		std::cin >> h1[i] >> a1[i] >> b1[i] >> c1[i] >> d1[i] >> e1[i] >> w1[i];
		A.push(i);
	}
	for(int i = 1; i <= m; i ++) {
		std::cin >> h2[i] >> a2[i] >> b2[i] >> c2[i] >> d2[i] >> e2[i] >> w2[i];
		B.push(i);
	}
	int cnt = 0;
	int nowa, nowb;
	while(!A.empty() && !B.empty() && cnt < k) {
		nowa = A.front();
		nowb = B.front();
		A.pop();
		B.pop();
		int p = std::max(0, a1[nowa] - c2[nowb]);
		int ma = std::max(0, b1[nowa] - d2[nowb]);
		int ww = 0;
		if(ea[nowa] >= e1[nowa])
			ww = w1[nowa];
		int hurt = std::max({p, ma, ww});
		if(p == hurt) {
			ea[nowa] ++;
			h2[nowb] -= p;
		}
		else if(ma == hurt) {
			ea[nowa] ++;
			h2[nowb] -= ma;
		}
		else {
			ea[nowa] -= e1[nowa];
			h2[nowb] -= ww;
		}
		A.push(nowa);
		nowa = A.front();
		if(h2[nowb] <= 0) {
			if(!B.empty()) {
				nowb = B.front();
				B.pop();
			}
			else break;
		}
		p = std::max(0, a2[nowb] - c1[nowa]);
		ma = std::max(0, b2[nowb] - d1[nowa]);
		ww = 0;
		if(eb[nowb] >= e2[nowb])
			ww = w2[nowb];
		hurt = std::max({p, ma, ww});
		if(p == hurt) {
			eb[nowb] ++;
			h1[nowa] -= p;
		}
		else if(ma == hurt) {
			eb[nowb] ++;
			h1[nowa] -= ma;
		}
		else {
			eb[nowb] -= e2[nowb];
			h1[nowa] -= ww;
		}
		B.push(nowb);
		if(h1[nowa] <= 0) A.pop();
		cnt ++;
	}
	if(!A.empty() && !B.empty()) 
		std::cout << "Draw" << '\n';
	else if(!A.empty())
		std::cout << "Alice" << '\n';
	else
		std::cout << "Bob" << '\n';
	return 0;
}

ps:我是会写答辩的xD

H. 字符串游戏(AC自动机)

思路

考虑对于A的字符串集合建立AC自动机,然后对于每个B给出的字符串枚举前缀,对于这些前缀,我们就可以在AC自动机上不断跳fail边得到可以匹配的后缀,并且是不重不漏的,这样就可以对于每一种匹配情况计算方案数。

在写代码时,其实对于AC自动机的板子对于统计答案的solve函数进行修改即可,每次从当前前缀到达的now点匹配的后缀去跳fail边,但是由于AC自动机的板子是在使用之后就被修改了,我在这里的做法是将它记录下修改的点,再给它复原即可。

Code

#include <bits/stdc++.h>

typedef long long ll;
typedef std::pair<int, int> PII;
const int N = 5e5 + 5;
const int mod = 1e9 + 7;

struct AC_Automation {
	int tr[N][26];
	int fail[N];
	ll dep[N];
	int vis[N];
	int cnt;
	std::queue<int> q;

	void init() {
		cnt = 0;
		memset(tr, 0, sizeof(tr));
		memset(dep, 0, sizeof(dep));
		memset(fail, 0, sizeof(fail));
		memset(vis, 0, sizeof(vis));
		while(!q.empty()) q.pop();
	}

	void insert(std::string s) {
		int len = s.length();
		int now = 0;
		for(int i = 0; i < len; i ++) {
			int v = s[i] - 'a';
			if(!tr[now][v]) {
				cnt ++;
				tr[now][v] = cnt;
				dep[cnt] = i + 1;
			}
			now = tr[now][v];
		}
		vis[now] ++;
	}

	void build() {
		for(int i = 0; i < 26; i ++) {
			if(tr[0][i]) {
				fail[tr[0][i]] = 0;
				q.push(tr[0][i]);
			}
		}
		while(!q.empty()) {
			int u = q.front();
			q.pop();
			for(int i = 0; i < 26; i ++) {
				if(tr[u][i]) {
					fail[tr[u][i]] = tr[fail[u]][i];
					q.push(tr[u][i]);
				}
				else 
					tr[u][i] = tr[fail[u]][i];
			}
		}
	}

	int solve(ll ans, int now, ll pos, ll l) {
		std::vector<PII> rec;
		for(int i = now; i && ~vis[i]; i = fail[i]) {
			rec.push_back({i, vis[i]});
			ll res = vis[i] * ((pos + 2 - dep[i]) % mod * (l - pos) % mod) % mod;
			ans = (ans % mod + res) % mod;
			vis[i] = -1;
		}
		for(auto [x, y] : rec)
			vis[x] = y;
		return ans;
	}
} AC;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int _ = 1;
	// std::cin >> _;
	while(_ --) {
		int n, m;
		std::cin >> n >> m;
		for(int i = 1; i <= n; i ++) {
			std::string s;
			std::cin >> s;
			AC.insert(s);
		}
		AC.build();
		for(int i = 1; i <= m; i ++) {
			std::string p;
			std::cin >> p;
			ll ans = 0;
			int now = 0;
			for(int j = 0; j < p.length(); j ++) {
				now = AC.tr[now][p[j] - 'a'];
				ans = AC.solve(ans, now, j, p.length());
			}
			std::cout << ans << '\n';
		}
	}
	return 0;
}

ps:自己学了三天AC自动机写出来的题,AC的时候还是很开心的,可恶,为什么之前没学AC自动机呀!

D. 金人旧巷市廛喧(网络流)

思路    

待补

Code

B. 终焉之茧(二分/三分)

思路

待补

Code

### 2023 CCPC河南区比题目及解析 #### 动态规划的应用——E.矩阵游戏 在2023年的CCPC河南站比中,有一道名为“矩阵游戏”的题目引起了广泛关注。该题的核心在于如何通过优化算法降低时间复杂度,从而实现高效求解。最初可以采用暴力递归的方式解决问题,但由于其指数级的时间复杂度,在大规模数据下显然不可行。因此,引入动态规划的思想成为必然选择[^1]。 以下是基于动态规划解决此问题的一个简单代码示例: ```python def dp_matrix_game(matrix, n, m): # 初始化dp数组 dp = [[0 for _ in range(m)] for __ in range(n)] # 边界条件初始化 dp[0][0] = matrix[0][0] # 填充dp表 for i in range(1, n): dp[i][0] = dp[i-1][0] + matrix[i][0] for j in range(1, m): dp[0][j] = dp[0][j-1] + matrix[0][j] for i in range(1, n): for j in range(1, m): dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + matrix[i][j] return dp[n-1][m-1] ``` 这段代码展示了如何利用二维动态规划来计算从左上角到右下角的最大路径和,其中`matrix`是一个给定的整数矩阵,而`n`和`m`分别表示矩阵的行数和列数。 #### 排列与质数问题分析——K题 另一道值得讨论的是关于排列与质数的问题(K题)。对于较小规模的情况(`n<10`),可以直接使用暴力枚举的方法找出符合条件的所有排列组合;而对于稍大一点的数据范围,则可以通过特定模式简化运算过程。具体而言,当输入参数位于区间 `[5,9]` 中时,只需依次打印从小到大的奇数序列后再接续偶数值即可满足题目需求[^2]。 例如,针对某个具体的测试样例 `n=7`, 输出应为:`1 3 5 7 2 4 6`. --- ### 参经验分享 参加此类竞不仅考验选手的技术实力,同时也对其心理素质提出了较高要求。回顾去年的比经历,团队成员之间缺乏有效沟通以及对未知领域探索不足成为了失利的主要原因所在。为了避免再次发生类似状况,建议未来参者们提前做好充分准备: - **熟悉常用算法模板**: 如图论、字符串处理等领域经典模型; - **加强合作意识培养**: 定期开展模拟训练活动增进默契程度; - **保持良好心态调整策略**: 面对难题不要轻易放弃尝试多种思路寻找突破口. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值