日常训练2025-1-16

日常训练2025-1-16

C. Add Zeros

rating:1500

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

思路(转化为图)

我们把公式化成 |a| = a_i + i - 1,即满足这个公式的位置会给长度加 i - 1

所以相当于从 a_i + i - 1 ----> a_i + i - 1 + i - 1 建一条有向边,跑一个 dfs 即可。

评述

写dfs,和bfs的时候一定要写vis数组,千万别相信愚蠢的直觉。

代码

#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::map<i64, bool> vis;
	std::map<i64, std::vector<i64>> mp;
	std::vector<i64> a(n+1);
	for (int i = 1; i <= n; i++){
		std::cin >> a[i];
		mp[a[i]+i-1].emplace_back(a[i]+2*(i-1));
	}

	i64 ans = n;
	auto dfs = [&](auto self, i64 p) -> void {
		vis[p] = 1;
		ans = std::max(ans, p);

		for (auto to : mp[p]){
			if (!vis[to]){
				self(self, to);
			}
		}

	};

	dfs(dfs, n);

	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);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

D. Robert Hood and Mrs Hood

rating:1400

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

思路(滑动窗口)

滑动窗口算法是在给定窗口大小的数组或字符串上统计窗口内部数据的操作。这道题给定了窗口大小是d,然后只需统计窗口内重叠的工作数量就行。r 表示窗口的头部,只负责添加重叠,l 表示窗口的尾部,只负责减少重叠。

代码

#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, d, k;
	std::cin >> n >> d >> k;

	std::map<int, int> mp;
	std::map<int, int> dic;
	for (int i = 0; i < k; i++){
		int u, v;
		std::cin >> u >> v;
		mp[u]++;
		dic[v]++;
	}

	int ans1 = -1, ans2 = -1;
	int maxx = 0;
	int minn = INT_MAX;

	int cur = 0;

	int l = 0, r = 0;
	while (r < n){
		r++;
		if (mp[r] != 0){
			cur += mp[r];
		}
		if (r - l + 1 > d){
			l++;
			if (dic[l-1] != 0){
				cur -= dic[l-1];
			}
		}

		if (l >= 1 && r - l + 1 == d){
			if (cur > maxx){
				maxx = cur;
				ans1 = l;
			}
			if (cur < minn){
				minn = cur;
				ans2 = l;
			}
		}
	}

	std::cout << ans1 << ' ' << ans2 << '\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;
}

E. Klee's SUPER DUPER LARGE Array!!!

rating:1400

https://codeforces.com/problemset/problem/2009/E

思路(二分)

答案只有两种情况,分界线左边刚好大于分界线右边,此时的答案是最小正数,还有就是分界线左边刚好分界线右边,此时答案是最大的负数。

所以我们可以根据二分找到最小的正数的分界线在哪,此分界线+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(){
	i64 n, k;
	std::cin >> n >> k;

	i64 sum = n * (k + k + n - 1) / 2;
	i64 ans = sum;

	i64 lo = 0, hi = n;
	while (lo < hi){
		i64 x = (lo + hi + 1) / 2;
		i64 s = x * (k + k + x - 1) / 2;
		if (s <= (sum - s)){
			lo = x;
		}else{
			hi = x - 1;
		}
	}

	for (auto x : {lo, lo + 1}){
		if (x > n) continue;
		i64 s = x * (k + k + x - 1) / 2;
		ans = std::min(ans, abs(s - (sum - s)));
	}

	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);
	int t = 1, i;
	std::cin >> t;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}

C. Guess The Tree

rating:1500

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

思路(模拟)

题目中给的那个公式,返回一个 x, 这个 x 其实就是 a b 的中点,所以相当于是二分操作。

模拟一下就能发现,随便拿两个节点 a b,a节点不变,b 节点不断换成询问后的回答节点 c,这样一定能找到 a 节点的一条边。n 的上界是1000,就算这颗二叉树是一条链(存在两节点距离最远)的情况下,也可以通过10次查找找出来这条边(2^10 > 1000),所以直接暴力找即可。

代码

#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;

	auto ask = [&](int a, int b) ->int{
		printf("? %d %d\n", a, b);
		fflush(stdout);
		int ret;
		scanf("%d", &ret);
		return ret;
	};

	std::vector<int> fa(n+1);
	for (int i = 2; i <= n; i++){
		int r = 1;
        // j < 10 就够用了,开15豪横。
		for (int j = 0; j < 15; j++){
			int g = ask(i, r);
			if (g == i) break;
			r = g;
		}
		fa[i] = r;
	}

	printf("!");
	for (int i = 2; i <= n; i++){
		printf(" %d %d", fa[i], i);
	}
	puts("");
	fflush(stdout);
}

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;
}

D - Strange Mirroring

rating:青色

https://atcoder.jp/contests/abc380/tasks/abc380_d

思路(思维题)

能想到二进制简直太强。

https://www.bilibili.com/video/BV1zyUiY8EFs/?spm_id_from=333.337.search-card.all.click&vd_source=4a339d299e165d8fe38b9926c5240eae

评述

非常棒的思维题

代码

#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(){
	std::string s;
	std::cin >> s;

	int len = s.size();


	auto C = [&](char x) -> char{
		if (x >= 'A' && x <= 'Z') return x - 'A' + 'a';
		else return x - 'a' + 'A';
	};

	int n;
	std::cin >> n;
	for (int i = 0; i < n; i++){
		i64 x;
		std::cin >> x;
		x--;
		if (__builtin_popcountll(x / len) & 1) std::cout << C(s[x % len]) << ' ';
		else std::cout << s[x%len] << ' ';
	}
}

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;
	for (i = 0; i < t; i++){
		solve();
	}
	return 0;
}
posted @ 2025-01-16 11:50  califeee  阅读(35)  评论(0)    收藏  举报