首页 > 其他分享 >2022 Shanghai Collegiate Programming Contest

2022 Shanghai Collegiate Programming Contest

时间:2022-11-01 15:33:55浏览次数:78  
标签:Contest int LL Shanghai Programming long ++ using dp

比赛链接:

https://codeforces.com/gym/103931

A. Another A+B Problem

题意:

给定一个等式和等式每个位置对应的颜色。
如果颜色是绿色,那答案的这一位也要是这个字符。
如果是紫色,那答案要有这个字符,但是不能在这一位上。
如果是黑色,那答案要不能有这个字符或者其它颜色的该字符都要在字符串中出现。
输出所有可能的等式及数量。

思路:

因为数量很小,直接暴力跑所有的结果,判断等式是否符合要求。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	string E, C;
	cin >> E >> C;
	map<char, int> B, P;
	for (int i = 0; i < 8; i ++ ){
		if (C[i] == 'B') B[E[i]] ++ ;
		if (C[i] == 'P') P[E[i]] ++ ;
	}
	auto check = [&](string s){
		map<char, int> cnt;
		for (int i = 0; i < 8; i ++ ){
			if (s[i] == E[i]){
				if (C[i] != 'G')
					return false;
			}
			else {
				if (C[i] == 'G')
					return false;
				cnt[s[i]] ++ ;
			}
		}
		for (char c = '0'; c <= '9'; c ++ ){
			if (B[c] && P[c] != cnt[c]) return false;
			if (!B[c] && P[c] > cnt[c]) return false;
		}
		return true;
	};
	vector <string> f;
	for (int i = 0; i <= 99; i ++ ){
		for (int j = 0; j <= 99; j ++ ){
			if (i + j >= 100) continue;
			string t = "";
			t += '0' + i / 10;
			t += '0' + i % 10;
			t += '+';
			t += '0' + j / 10;
			t += '0' + j % 10;
			t += '=';
			t += '0' + (i + j) / 10;
			t += '0' + (i + j) % 10;
			if (check(t)) f.push_back(t);
		}
	}
	cout << f.size() << "\n";
	for (auto x : f)
		cout << x << "\n";
	return 0;
}

E. Expenditure Reduction

题意:

给定两个字符串 \(s\) 和 \(f\),找到 \(s\) 最短的一个子串,使 \(f\) 是它的子序列。

思路:

brute force

直接预处理每个字母往后跳到哪里,即这个字母下一次出现在哪里,然后暴力维护即可。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
map <char, int> Map;
void init(){
	int cnt = 0;
	for (char c = 'a'; c <= 'z'; c ++ )
		Map[c] = ++ cnt;
	for (char c = '0'; c <= '9'; c ++ )
		Map[c] = ++ cnt;
}
void solve(){
	string s, f;
	cin >> s >> f;
	int n = s.size(), m = f.size();
	vector < vector <int> > to(37, vector <int>(n + 1, n));
	vector <int> pos(37, n);
	for (int i = n - 1; i >= 0; i -- ){
		to[Map[s[i]]][i] = pos[Map[s[i]]];
		pos[Map[s[i]]] = i;
	}
	vector <int> now(m);
	int len = 1e9, p = 0;
	for (int i = 0, j = 0; i < n; i ++ ){
		if (s[i] == f[j]){
			now[j] = i;
			for (int k = j - 1; k >= 0; k -- ){
				while(to[Map[f[k]]][now[k]] < now[k + 1]){
					now[k] = to[Map[f[k]]][now[k]];
				}
			}
			if (j == m - 1){
				if (i - now[0] + 1 < len){
					len = i - now[0] + 1;
					p = now[0];
				}
			}
			else j ++ ;
		}
	}
	cout << s.substr(p, len) << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	init();
	int T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

dp

设 \(dp_{i, j}\) 表示 \(s\) 以第 \(i\) 个字母结尾,\(f\) 的第 \(j\) 个字母结尾的子序列最早在 \(s\) 的哪里出现过。
那么 \(i - dp_{i, m - 1}\) 就是最终字符串的长度,找到最小的就行。

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
	string s, f;
	cin >> s >> f;
	int n = s.size(), m = f.size();
	vector<vector<int>> dp(n, vector<int>(m, -1));
	for (int i = 0; i < n; i ++ ){
		if (s[i] == f[0]) dp[i][0] = i;
		if (i != 0){
			for (int j = 0; j < m; j ++ ){
				if (j != 0 && s[i] == f[j])
					dp[i][j] = max(dp[i][j], dp[i - 1][j - 1]);
				dp[i][j] = max(dp[i][j], dp[i - 1][j]);
			}
		}
	}
	int L = 0, R = n - 1;
	for (int i = 0; i < n; i ++ ){
		if (dp[i][m - 1] != -1 && i - dp[i][m - 1] < R - L){
			L = dp[i][m - 1];
			R = i;
		}
	}
	cout << s.substr(L, R - L + 1) << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

G. Gua!

题意:

玩家在 \(S\) 内用枪造成了 \(D\) 点伤害,已知枪每发的最大伤害为 \(B\),\(RPM\)(每分钟射速)为 \(R\),问这人是不是开挂。

思路:

得到射速为 \(R * S / 60 + 1\),因为打一枪需要等 \(\frac{1}{R}\) 分钟,所以最多可以多打一枪,但是这要建立在 \(R > 0\) 的基础上。判断一下造成伤害是否大于这个值即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
	int B, R, D, S;
	cin >> B >> R >> D >> S;
	if (D > (R * S / 60 + 1 * (R > 0)) * B) cout << "gua!\n";
	else cout << "ok\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

H. Heirloom Painting

题意:

长为 \(n\) 的环,颜色从 1 到 \(m\),每次可以涂连续的 \(k\) 个格子使其变成一个颜色,问最少几步能涂成题目所给的那样的环。

思路:

最后一步肯定将连续的 \(k\) 个格子涂成一个颜色了,所以先找有没有连续的 \(k\) 个及以上的格子。因为是环,要将后面与开头相同的数字放到开头先。
对于一段长为 \(len\) 的区间,最少花 \(\lceil \frac{len}{k} \rceil\) 步,由此可以计算出答案。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
	int n, m, k;
	cin >> n >> m >> k;
	vector <int> c(n);
	for (int i = 0; i < n; i ++ )
		cin >> c[i];
	int R = n - 1;
	vector <int> a;
	for (; R >= 0 && c[R] == c[0]; R -- )
		a.push_back(c[R]);
	for (int i = 0; i <= R; i ++ )
		a.push_back(c[i]);
	vector <int> len;
	for (int i = 0; i < n; i ++ ){
		int j = i + 1;
		while(j < n && a[j] == a[j - 1]){
			j ++ ;
		}
		len.push_back(j - i);
		i = j - 1;
	}
	if (*max_element(len.begin(), len.end()) >= k){
		LL ans = 0;
		for (auto x : len)
			ans += (x + k - 1) / k;
		cout << ans << "\n";
	}
	else{
		cout << "-1\n";
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

I. It Takes Two of Two

题意:

有 \(n\) 个点,每次随机选取两个点进行连接,当该边满足条件时才会被添加进图。
1.图中无重边和自环。
2.每个点的度不超过 2。
问不能再添加边时的添加次数期望。

思路:

考虑可以添加的情况,只有单独的点和链,链又可分为两类,长为 1,以及长度大于 1 的(可以头尾相连,变成环)。记录三者的数量,进行转移。
当前这种情况的期望即 \(\frac{所有子情况的期望和 + 1)}{当前情况的概率}\)。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 210;
double dp[N][N][N];
bool vis[N][N][N];
int n;
double dfs(int i, int j, int k){
	if (vis[i][j][k]) return dp[i][j][k];
	if (i == 0 && j == 0 && k == 0) return dp[i][j][k] = 0;
	if (i == 1 && j == 0 && k == 0) return dp[i][j][k] = 0;
	if (i == 0 && j == 1 && k == 0) return dp[i][j][k] = 0;
	double ans = 0, sum = 0;
	if (i >= 2){  //点和点
		double p = 1.0 * i * (i - 1) / (n * n);
		sum += p;
		ans += dfs(i - 2, j + 1, k) * p;
	}
	if (i >= 1 && j >= 1){  //点和长度为 1 的链
		double p = 2.0 * i * 2 * j / (n * n);
		sum += p;
		ans += dfs(i - 1, j - 1, k + 1) * p;
	}
	if (i >= 1 && k >= 1){  //长度为 1 的链和长大于 1 的链
		double p = 2.0 * i * 2 * k / (n * n);
		sum += p;
		ans += dfs(i - 1, j, k) * p;
	}
	if (j >= 2){  //两条短链合成长链
		double p = 1.0 * 2 * j * (2 * j - 2) / (n * n);
		sum += p;
		ans += dfs(i, j - 2, k + 1) * p;
	}
	if (j >= 1 && k >= 1){  //短链和长链合成长链
		double p = 2.0 * 2 * j * 2 * k / (n * n);
		sum += p;
		ans += dfs(i, j - 1, k) * p;
	}
	if (k >= 2){  //两条长链合成
		double p = 1.0 * 2 * k * 2 * (k - 1) / (n * n);
		sum += p;
		ans += dfs(i, j, k - 1) * p;
	}
	if (k >= 1){  //形成环
		double p = 1.0 * 2 * k / (n * n);
		sum += p;
		ans += dfs(i, j, k - 1) * p;
	}
	vis[i][j][k] = true;
	return dp[i][j][k] = (ans + 1) / sum;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	cin >> n;
	cout << fixed << setprecision(9) << dfs(n, 0, 0) << "\n";
	return 0;
}

L. Last Warning of the Competition Finance Officer

题意:

给定一个字符串 \(s\),一个包含 \(n\) 个字符串的字典,字典中的每个字符串有一个权值,求出 \(s\) 的每个前缀的权值。
一个字符串的权值为它所有组合的权值之和。
一个组合中,每个字符串都要在字典中,或者为空。空串权值为 1。

思路:

定义 \(dp_i\) 为以第 \(i\) 个字符结尾的字符串的权重。容易得到 \(dp_i = \sum_{j = 1}^{n} dp_{i - len_j} * w_j\)。
直接转移显然超时,想到通过AC自动机进行转移,将字典中的字符串都放到自动机中然后对模式串进行匹配。
但在 \(fail\) 树上跑每个子串数量太大了,只需要匹配有权值的答案,所有要让每一个串向存在的串进行转移。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mod = 998244353, N = 2e5 + 10;
struct ACA{
	LL ch[N][26], fail[N], idx = 0, len[N], w[N], f[N], id[N], Next[N];
	void insert(string s, LL val){
		LL u = 0;
		for (int i = 0; i < s.size(); i ++ ){
			LL v = s[i] - 'a';
			if (!ch[u][v]) ch[u][v] = ++ idx;
			u = ch[u][v];
		}
		w[u] = val;
		len[u] = s.size();
	}
	void get_fail(){
		iota(id, id + idx + 1, 0);
		queue <LL> q;
		for (int i = 0; i < 26; i ++ ){
			if (ch[0][i]){
				fail[ch[0][i]] = 0;
				q.push(ch[0][i]);
			}
		}
		while (q.size()){
			LL u = q.front();
			q.pop();
			Next[u] = id[fail[u]];
			if (!w[u]) id[u] = Next[u];  //向有权值的点去跳
			for (int v = 0; v < 26; v ++ ){
				if (ch[u][v]){
					fail[ch[u][v]] = ch[fail[u]][v];
					q.push(ch[u][v]);
				}
				else ch[u][v] = ch[fail[u]][v];
			}
		}
	}
	void query(string t){
		LL u = 0;
		f[0] = 1;
		for (int i = 0; i < t.size(); i ++ ){
			u = ch[u][t[i] - 'a'];
			f[i + 1] = f[i];
			for (LL v = id[u]; w[v]; v = Next[v]){
				f[i + 1] = (f[i + 1] + f[i + 1 - len[v]] * w[v] % mod) % mod;
			}
		}
		for (int i = 1; i <= t.size(); i ++ )
			cout << f[i] << " \n"[i == t.size()];
	}
}aca;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	string s;
	cin >> s;
	int n;
	cin >> n;
	for (int i = 0; i < n; i ++ ){
		string t;
		LL val;
		cin >> t >> val;
		aca.insert(t, val);
	}
	aca.get_fail();
	aca.query(s);
	return 0;
}

M. My University Is Better Than Yours

题意:

给定 \(m\) 个 \(n\) 个大学的排行榜,从左向右由高到低,问每个大学比多少个大学的排名高。

思路:

如果遍历每个排行榜的前 \(i\) 个,记出现的大学数量为 \(k\),当 \(k = i\) 时,说明前面只有这些大学,它们的排名都比后面所有的大学高,由此可以记录答案。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int n, m;
	cin >> n >> m;
	vector<vector<int>> s(m, vector<int>(n));
	for (int i = 0; i < m; i ++ )
		for (int j = 0; j < n; j ++ )
			cin >> s[i][j];
	vector<bool> vis(n + 1);
	vector<int> ans(n + 1);
	queue<int> q;
	for (int j = 0, num = 0, res = 1; j < n; j ++ ){
		for (int i = 0; i < m; i ++ ){
			if (!vis[s[i][j]]){
				vis[s[i][j]] = true;
				q.push(s[i][j]);
				num ++ ;
			}
		}
		if (num == j + 1){
			while(!q.empty()){
				int x = q.front();
				q.pop();
				ans[x] = n - res;
			}
			res = num + 1;
		}
	}
	for (int i = 1; i <= n; i ++ )
		cout << ans[i] << " \n"[i == n];
	return 0;
}

N. Nine Is Greater Than Ten

题意:

比较给定字符串大小。

代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	string a, b;
	cin >> a >> b;
	if (a > b) cout << a << ">" << b << "\n";
	else if (a == b) cout << a << "=" << b << "\n";
	else cout << a << "<" << b << "\n";
	return 0;
}

标签:Contest,int,LL,Shanghai,Programming,long,++,using,dp
From: https://www.cnblogs.com/Hamine/p/16829937.html

相关文章

  • Atcoder Beginner Contest 273(A~E+G)
    E场上想麻烦了,调根号分治浪费了点时间;F涉及后缀数组,还没有系统学;G场上没来的及看,也沾点数学,估计场上也想不到(不好,不好。赛时A神笔数组求和;B迷你模拟;C分别找到奇......
  • 2022 China Collegiate Programming Contest (CCPC) Guilin Site
    比赛链接2022ChinaCollegiateProgrammingContest(CCPC)GuilinSiteC.ArrayConcatenation给定一个长度为\(n\)的数组\(b_i\),有两种操作变换:\(b^{\prime}=\l......
  • [Leetcode Weekly Contest]317
    链接:LeetCode[Leetcode]2455.可被三整除的偶数的平均值给你一个由正整数组成的整数数组nums,返回其中可被3整除的所有偶数的平均值。注意:n个元素的平均值等于n个......
  • The 2021 ICPC Asia Shenyang Regional Contest
    The2021ICPCAsiaShenyangRegionalContest我们按难易程度来,E,F<B,J<H,I,L,ME.EdwardGaming,theChampion直接输出edgnb子字符串数量。F.EncodedStringsI分......
  • Team Extra Contest 2022-10-21补题
    D.38parrots线段树#include<bits/stdc++.h>usingnamespacestd;#defineIOSios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)#definerep(a,b......
  • AtCoder Beginner Contest 275 D~F
    D-YetAnotherRecursiveFunction思路:直觉需要用到的数字大多数是重复的,所以记忆化了一下,测了一下1e18的数据,跑的飞快,直接交了,6ms。#include<bits/stdc++.h>#def......
  • Linear Algebra and Linear Programming Notes
    LinearAlgebraandLinearProgrammingDualityTheoryPrimalanddualproblemsofLPs如何理解?\[\bar{x}=\left(\begin{array}{l}x\\y\end{array}\right),\quad......
  • AtCoder Beginner Contest 275 E F
    E-Sugoroku4题意:从0走到n,有k次操作,每次会丢出一个骰子,骰子上的数字为\([1,m]\),等概率出现问到达n的概率思路:\(dp[i][k]\)表示用了k次操作到达位置i的概......
  • Atcoder Beginner Contest 275(A~F)
    我好菜啊……又没切掉F,40+min切掉A~E成功滚粗。希望能越打越好吧……赛时A求序列最大值,B简单计算,C数正方形,跳过。D递推不太行,N的范围有点大。但是除法的转移......
  • AtCoder Beginner Contest 275
    咕咕咕咕咕咕。G-InfiniteKnapsack做法1-二分假设第\(i\)个物品选了\(x_i\)个,\(x_i\)为非负整数,有\[\lim_{x\to+\infin}\frac{f(x)}{x}=\frac{\sum_ic_......