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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值