Codeforces Round 957 (Div 3)(A—G题解)

Codeforces Round 957 (Div. 3)

2024-07-12 —yimg

A

签到题

代码:

#include<bits/stdc++.h>
using namespace std;
void work()
{
	vector<int> a(3);
	for(int i = 0; i < 3; ++i) cin >> a[i];
	int t = 5;
	while(t--){
		sort(a.begin(), a.end());
		a[0]++;
	}
	cout << a[0]*a[1]*a[2] << '\n';
}
int main()
{
	int t;
	cin >> t;
	while(t--)
		work();
}

B

签到题

代码:

#include<bits/stdc++.h>
using namespace std;
void work()
{
	int n, k;
	cin >> n >> k;
	vector<int> a(k);
	for(auto &i: a) cin >> i;
	sort(a.begin(), a.end());
	int ans = 0;
	for(int i = 0; i < k - 1; ++i){
		ans += a[i] - 1;
	}
	cout << ans + n - a[k - 1] << '\n';
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();
}

C

签到题

代码:

#include<bits/stdc++.h>
using namespace std;
void work()
{
	int n, m, k;
	cin >> n >> m >> k;
	vector<int> a(n + 1);
	int j = 1;
	for(int i = n; i > m; --i, ++j){
		a[j] = i;
	}
	for(int i = 1; i <= m; ++i, ++j){
		a[j] = i;
	}
	for(int i = 1; i <= n; ++i)
		cout << a[i] << " ";
	cout << "\n";
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();
}

D

题意:

ErnKor过河,这条河有n个格子,L代表平台可以跳往[L,L + m]的任意位置,W代表水,每经过一格水要消耗一个体力,最开始有k个体力,也就是最多经过k格水,C代表鳄鱼,不能待在C所在的格子。给定n,m,k 和一个代表河流的字符串,问能否到达对岸

1m10,0k2105,1n2105

思路:

  • 比较容易想到的是用贪心来做,如果在能跳往下一个平台,跳往下一个平台一定是最优操作,如果不能,跳满m格是最优操作,其中会判NO的情况是在水中遇到’C’或者体力不够用,复杂组O(n + k)

  • dp来解决问题也很浅显,我们记录到当前格子要消耗的最小体力,我们当前的格子可能是由前面的格子跳过来的,或者由上一个水游过来的。 鳄鱼初始为极大值后不用管,因为我们不能通过‘C’转移。复杂度为O(nm)。

    dpi=min{dpj0ijm,sj=Ldpi1+1si1=W

代码:

贪心
#include<bits/stdc++.h>
using namespace std;
void work()
{
	int n, m, k;
	cin >> n >> m >> k;
	string s;
	cin >> s;
	for(int l = -1, r = 0; r <= s.length(); ++r){
		if(l == s.length()) break;
		if(l + m < r){
			l = l + m;
			if(s[l] == 'C'){
				cout << "NO\n";
				return;
			}
			while(1){
				k--;
				++l;
				if(s[l] == 'C' || k < 0){
					cout << "NO\n";
					return;
				}
				if(l == s.length() || s[l] == 'L'){
					r = l;
					break;
				}
			}
		}
		else if(r == s.length() || s[r] == 'L'){
				if(l + m >= r){
					l = r;
				}
			}
		
	}
	cout << "YES\n";
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();
}
DP
#include<bits/stdc++.h>
using namespace std;
void work()
{
	int n, m, k;
	cin >> n >> m >> k;
	string s;
	cin >> s;
	s = "L" + s + "L";
	vector<int> f(n + 5, 1e9 + 7);
	f[0] = 0;
	for(int i = 1; i <= n + 1; ++i){
		
		for(int j = 1; j <= m; ++j){
			if(i - j >= 0 && s[i - j] == 'L')
				f[i] = min(f[i], f[i - j]);
		}
		if(s[i - 1] == 'W') f[i] = min(f[i], f[i - 1] + 1);
	}
	if(f[n + 1] <= k) cout << "YES\n";
	else cout << "NO\n";
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();
}

E

题意:

给定n, 问有多少对符合要求的数对(a, b)满足 nab=f(n,a,b)

定义f(n,a,b)为将n个a顺序拼在一起,删去后b位剩下的数

1a104,1bmin(104,na)

思路:

a和b的范围都很小,直接枚举a,对于此a我们限制b的范围,b=alen(n)len(f) , 对每个a我们枚举b[alen(n)1,alen(n)5] 验证答案。

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void work()
{
	int n;
	cin >> n;
	string chn = to_string(n);
	string s;
	int ln = chn.length();
	for(int i = 1; i <= 10; ++i) s += chn;
	vector<pair<int, int>> ans;
	const int N = 1e4;
	for(int a = 1; a <= N; ++a){
		for(int b = max(a*ln-5, 1); b <= min(a*ln-1, min(N, a*n)); ++b){
			int num = 0;
			for(int i = 0; i < ln*a-b; ++i) num = num * 10 + s[i] - '0';
			if(n*a-b == num) ans.push_back({a,b});
		}
	}
	cout << ans.size() << '\n';
	for(auto v : ans)
		cout << v.first << " " << v.second << '\n';
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();
}

F

题意:

一个段(l,r) 如果不存在索引 i1<i2<<ikli1,ikr 使得 ai1ai2aik=x

则称这个段为坏段,每个元素属于且只属于一个段,且分段后,所有的段都是坏段,问坏段的最小数量

思路:

贪心地分段,如果一个元素可以分到上一个段就把它放到上一个段,否则分一个新段。

实现上,可以维护使当前段乘积为x的元素(也就是因子),要避免重复可以用set,复杂度为O(nlog(D(x)))

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
void work()
{
	int n, x;
	cin >> n >> x;
	vector<int> a(n + 1);
	for(int i = 1; i <= n; ++i) cin >> a[i];
	set<int> s;
	vector<int> tmp;
	int ans = 1;
	s.insert(x);
	for(int i = 1; i <= n; ++i){
		if(a[i] == 1) continue;	
		tmp.clear();
		for(auto el : s) {
			if(el%a[i] == 0)
				tmp.push_back(el/a[i]);
		}	
		for(auto el : tmp) s.insert(el);
		if(s.find(1) != s.end()){
			ans++;
			s.clear();
			s.insert(x);
			s.insert(x/a[i]);
		}
	}
	cout << ans << '\n';
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
		work();
}

G

题意:

MEOW(a)的值,MEOW(a)MEX(b,|b|+1) 的和,b为a的所有不同子集

a为长度为n的数组{1,2,3,…, n}

MEX(S,k) 为不包含S中元素的第k大的正整数

思路:

当我们选k个数为子集时:

  • 2k+1>n 时,MEX值固定为2k+1 ,子集有 Cnk
  • 2k+1n 时,MEX值不固定,但是我们可以枚举MEX值,令MEX值为m,我们要算出m-1个数有多少种选法
    • 选比m小的数要从[1,m1] 中选m1k个, 有Cm1m1k
    • 选比m大的数要从[m+1,n] 中选2k+1m 个, 有Cnm2k+1m

复杂度为O(n2)

实现上,求组合数时要用到乘法逆元,预处理好即可

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5000;
const int mod = 1e9 + 7;
ll fac[N + 5], inv[N + 5];
void init()
{
	fac[0] = fac[1] = 1;
	for(int i = 1; i <= N; ++i)
		fac[i] = fac[i - 1] * i % mod;
	auto qpow = [&](ll base, ll p){
		ll res = 1;
		while(p){
			if(p&1) res *= base, res %= mod; 
			base *= base; base %= mod;
			p >>= 1;
		}
		return res;
	};
	inv[N] = qpow(fac[N], mod - 2);
	for(int i = N - 1; i >= 0; --i)
		inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(int a, int b){
	if(a < 0 || b < 0 || a - b < 0) return 0;
	return fac[a] * inv[b] % mod * inv[a-b] % mod;
}
void work()
{
	int n;
	cin >> n;
	ll ans = 1;
	for(int k = 1; k <= n; ++k){
		if(2*k+1 > n){
			ans += (2*k+1) * C(n, k) % mod;
			ans %= mod;
			continue;
		}
		for(int m = k + 1; m <= 2 * k + 1; ++m){
			ans += m * C(m-1, m-1-k) % mod * C(n-m, 2*k+1-m) % mod;
			ans %= mod;
		}
	}
	cout << ans << '\n';
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	init();
	while(t--)
		work();
}
posted @   _yimg  阅读(126)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示