日常训练2025-1-14

日常训练2025-1-14

C. MEX Cycle

rating:1500

https://codeforces.com/contest/2049/problem/C

思路

注意这是一道构造题。

用xy将环分成两段,为保证xy符合条件,要先给xy的权值设置成01,然后根据两段链的长度的奇偶有两种填数方案。偶数直接01填,奇数要先填一个2然后再随机填。

评述

代码写到红温

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n, x, y;
	std::cin >> n >> x >> y;

	std::vector<int> v(n+1, -1);

	v[x%n] = 1, v[y%n] = 0;
	if ((y - x - 1) % 2 == 1){
		v[x+1] = 2;
		for (int i = y - 1; i > x + 1; i--){
			v[i] = v[(i+1)%n] ^ 1;
		}
	}else{
		for (int i = y - 1; i > x; i--){
			v[i] = v[(i+1)%n] ^ 1;
		}
	}

	if ((n - (y - x + 1)) % 2 == 0){
		for (int i = (y + 1) % n, j = 0; j < (n - (y-x+1)); j++, i++, i %= n){
			v[i] = v[(i+n-1)%n] ^ 1;
		}
	}else{
		v[(x+n-1)%n] = 2;
		for (int i = (y + 1) % n, j = 0; j < (n-(y-x+1))-1; j++, i++, i %= n){
			v[i] = v[(i+n-1)%n] ^ 1;
		}
	}

	for (int i = 1; i < n; i++){
		std::cout << v[i] << ' ';
	}
	std::cout << v[0] << ' ';
	std::cout << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

C. Sums on Segments

rating:1600

https://codeforces.com/problemset/problem/2043/C

思路(子数组和的最大值和最小值+Trick)

假设题目中没有出现特殊数字,那么答案就是子数组最大值和子数组最小值,以及它们范围中的所有整数,因为序列中只有1或者-1。

假设题目中出现了特殊数字,那么答案分为两部分:

  1. 不需要加上特殊数字也能得到的数:即是特殊数字左右的子数组最小值,和特殊数字左右的子数组最大值,以及两个最值中间的每个数。
  2. 加上特殊数字的话:就是求特殊数字左边的数组的包含特殊数字的子数组最大值和最小值,以及特殊数字右边的数组的包含特殊数字的子数组最大值和最小值,最后统计范围中的数。

评述

当数组中的增量是-1和1是,数组中的增加和减少是连续的。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n, num = -1;
	std::cin >> n;

	std::set<int> ret;
	std::vector<int> v(n+1);
	for (int i = 1; i <= n; i++) {
		std::cin >> v[i];
		if (v[i] != -1 && v[i] != 1){
			num = i;
		}
	}

	int cntmax = 0;
	int cntmin = 0;
	int minn = 0;
	int maxn = 0;

	if (num == -1){
		for (int i = 1; i <= n; i++){
			cntmax += v[i];
			cntmin += v[i];
			minn = std::min(cntmin, minn);
			maxn = std::max(cntmax, maxn);
			if (cntmax < 0) cntmax = 0;
			if (cntmin > 0) cntmin = 0;
		}
	}else{
		for (int i = 1; i <= num - 1; i++){
			cntmax += v[i];
			cntmin += v[i];
			minn = std::min(minn, cntmin);
			maxn = std::max(maxn, cntmax);
			if (cntmax < 0) cntmax = 0;
			if (cntmin > 0) cntmin = 0;
		}
		cntmax = 0;
		cntmin = 0;
		for (int i = num + 1; i <= n; i++){
			cntmax += v[i];
			cntmin += v[i];
			minn = std::min(minn, cntmin);
			maxn = std::max(maxn, cntmax);
			if (cntmax < 0) cntmax = 0;
			if (cntmin > 0) cntmin = 0;
		}
	}

	for (int i = minn; i <= maxn; i++) ret.insert(i);

	int ans = 0;
	int premax = 0;
	int premin = 0;
	int endmax = 0;
	int endmin = 0;
	if (num != -1){
		for (int i = num - 1; i >= 1; i--){
			ans += v[i];
			premax = std::max(premax, ans);
			premin = std::min(premin, ans);
		}
		ans = 0;
		for (int i = num + 1; i <= n; i++){
			ans += v[i];
			endmax = std::max(endmax, ans);
			endmin = std::min(endmin, ans);
		}

		for (int i = v[num] + premin + endmin; i <= v[num] + premax + endmax; i++){
			ret.insert(i);
		}
	}

	std::cout << ret.size() << '\n';
	for (auto e : ret) std::cout << e << ' ';

	std::cout << '\n';

}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

B. Move Back at a Cost

rating:1600

https://codeforces.com/problemset/problem/2046/B

思路(贪心+优先级队列)

贪心的想,为了使最后的词典序最小,肯定是数字越小的放越前面。考虑到,当一个小的数字被拿到前面时,它的左边的所有数字都会被+1,并且塞到最后。所以我们用一个变量 pos 记录上一个被拿到前面的数字的最大下标是多少,后续拿出来的每一个数只要下标小于 pos ,则说明一定被操作过,则数值+1,并且成为新的最末尾的数被加到最后。

我们可以证明每一个数最多被操作一次,多操作一次就不优了。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define MOD 998244353
using i64 = long long;
const int N = 1e5+5;

void solve(){
	int n;
	std::cin >> n;

	std::vector<int> a(n+1);
	std::priority_queue<pii, std::vector<pii>, std::greater<> > q;
	for (int i = 1; i <= n; i++){
		std::cin >> a[i];
		q.push({a[i], i});
	}

	int pos = 0;
	int cnt = n;
	while (!q.empty()){
		auto [x, y] = q.top();
		q.pop();
		if (y < pos){
			q.push({x+1, ++cnt});
		}else{
			pos = std::max(pos, y);
			std::cout << x << ' ';
		}
	}
	std::cout << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

G. Natlan Exploring

rating:2000

https://codeforces.com/contest/2037/problem/G

思路(容斥优化+计数DP)

经典求路径数目的计数DP,不过这道题核心考点是DP优化,容斥优化DP

https://www.cnblogs.com/personaowl/p/18560766,这篇题解很透彻了。

评述

利用线性筛是可以求每个数的最小质因子的,可以通过这样的方式优化质因数分解。

代码

#include <bits/stdc++.h>

typedef std::pair<long long, long long> pll;
typedef std::pair<int, int> pii;
#define INF 0x3f3f3f3f
#define mod 998244353
using i64 = long long;
const int N = 1e6+5;

int sum[N];// 标记位置
std::vector<int> prime;//质数数组
std::vector<int> S(N);
void op(int n)
{
    sum[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (!sum[i])
            prime.push_back(i), sum[i] = i;
        for (int j : prime)
        {
            if (j * i > n)
                break;
            sum[j * i] = j;
            if (i % j == 0)
                break;
        }
    }
}

void solve(){
	int n;
	std::cin >> n;

	std::vector<int> a(n+1);
	std::vector<std::vector<int>> g(n+1, std::vector<int>());
	for (int i = 1; i <= n; i++) std::cin >> a[i];

	// std::cout << sum[2] << ' ' << sum[6] << ' ' << sum[3] << '\n';
	for (int i = 1; i <= n; i++){
		int tmp = a[i];
		while (tmp != 1){
			int v = sum[tmp];
			g[i].push_back(v);
			while(tmp % v == 0) tmp /= v;
		}
	}

	std::vector<int> f(n+1);
	f[1] = 1;
	for (int i = 1; i <= n; i++){
		int cnt = g[i].size();
		std::vector<int> v;
		for (int j = 1; j < (1 << cnt); j++){
			int mul = 1, sgn = -1;
			for (int k = 0; k < cnt; k++){
				if ((j >> k) & 1){
					mul *= g[i][k];
					sgn *= -1;
				}
			}
			v.push_back(mul);
			f[i] += sgn * S[mul] % mod;
			f[i] %= mod;
		}
		for (auto e : v){
			S[e] += f[i];
			S[e] %= mod;
		}
	}

	int ans = (f[n] + mod) % mod;
	std::cout << ans << '\n';
}

signed main()
{
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout<<std::setiosflags(std::ios::fixed)<<std::setprecision(2);

	op(N);
	int t = 1, i;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

D. Sharky Surfing

rating:1400

https://codeforces.com/problemset/problem/2037/D

思路(贪心)

很明显的贪心题,唯一需要注意的是我们是可以回去捡已经跳过的障碍前的能量的。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

void solve() {
	int n, m, L;
	cin >> n >> m >> L;

	std::vector<int> l(n), r(n), x(m), v(m);
	for (int i = 0; i < n; i ++) cin >> l[i] >> r[i];
	for (int i = 0; i < m; i ++) cin >> x[i] >> v[i];

	multiset<int> up;
	int E = 1, res = 0;
	for (int i = 0, j = -1; i < n; i ++) {
		while (j + 1 < m && x[j + 1] < l[i]) j ++, up.insert(v[j]);
		while (up.size() && E <= r[i] - l[i] + 1) {
			E += *up.rbegin(), res ++;
			up.erase( -- up.end());
		}
		if (E <= r[i] - l[i] + 1) {
			cout << -1 << endl;
			return;
		}
	}

	cout << res << endl;
}

signed main() {
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	int dt;
	cin >> dt;

	while (dt --)
		solve();

	return 0;
}
posted @ 2025-01-14 10:50  califeee  阅读(33)  评论(0)    收藏  举报