首页 > 其他分享 >Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2)

Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2)

时间:2023-08-28 18:12:37浏览次数:45  
标签:std Space int Contest long tag solve Div 翻转

A. 给三个数 \(x\) \(y\) \(n\) 。需要构造一个长度为 \(n\) 的数组满足以下条件

  1. \(a_1 = x\), \(a_n = y\) 。
  2. \(a\) 严格递增。
  3. 定义 \(b_i = a_{i + 1} - a_{i}\) ,\(b\) 严格递减。

显然前两个条件非常宽松,定义好起始点,让 \(a\) 严格单调递增即可。
显然 \(b\) 是 \(a\) 的差分数组,从后往前贪心地让 \(b_{n - 1} = 1,b_{n - 2} = 2, \cdots, b_2 = n - 2\) 构造出 \(a_2\) 到 \(a_n\) 即可。然后只需要判断 \(a_2 - a_1\) 满足 \(\geq n - 1\) 时合法。

view
#include <bits/stdc++.h>

typedef long long ll;

void solve() {
	int x, y, n; std::cin >> x >> y >> n;
	std::vector<int> a(n+1);
	a[1] = x; a[n] = y;
	for (int i = n - 1, cur = 1; i >= 2; --i, cur++) {
		a[i] = a[i + 1] - cur;
	}
	if (a[2] - a[1] >= n - 1) for (int i = 1; i <= n; i++) std::cout << a[i] << " \n"[i==n];
	else std::cout << -1 << '\n';
}

int main() {
	int _ = 1; std::cin >> _;
	while ( _-- ) { solve(); }
	return 0;
}

B. 给一个长为 \(n\) 的字符串 \(s\) 和一个正整数 \(k, (k - 1 \leq n)\) 。允许做任意次以下步骤。

  1. 交换第 \(i\) 位和第 \(i + 2\) 位字符
  2. \(reverse\) 第 \(i\) 位到 \(i + k - 1\) 位的字符

经过任意次上次操作后使 \(s\) 字典序最小。

  1. 允许无限次交换次数的情况下:第一步操作意味着可以任意排列偶数位和奇数位上的字符。
  2. 允许无限次 \(reverse\) 的情况下:
    1. \(k \& 1\) 即 \(revrse\) 段的长度为奇数时时不会改变奇偶位。答案唯一。
    2. \(!k \& 1\) 即 \(revrse\) 段的长度为偶数时
      1. \(k = n\) ,即整段 \(reverse\) ,互换所有的奇偶位,此时答案为两种选一。(虽然这题没这个条件,没意思)
      2. \(1 \leq k \leq n - 1\) ,可以互换任意一对奇数位元素和偶数位元素。

故讨论 \(k\&1\) ,\(!k\&1\ and\ k=n\),\(!k\&1\ and\ k<n\) 三种情况即可。

view
#include <bits/stdc++.h>

typedef long long ll;
char s[2000005], s_odd[2000005], s_even[2000005];
void solve() {
	int n, k; std::cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		std::cin >> s[i];
		if (i & 1) s_odd[(i+1)/2] = s[i];
		else s_even[i/2] = s[i];
	}
	std::string ans = "";
	if (k & 1) {
		std::sort(s_odd + 1, s_odd + (n+1)/2 + 1, [&](char a, char b){
			return a < b;
		});
		std::sort(s_even + 1, s_even + n/2 + 1, [&](char a, char b){
			return a < b;
		});
		for (int i = 1; i <= n/2; i++) ans += s_odd[i], ans += s_even[i];
		if (n&1) ans += s_odd[n/2+1];
	}
	else {
		if (k == n) {
			std::string ts1 = "", ts2 = "";
			
			std::sort(s_odd + 1, s_odd + (n+1)/2 + 1, [&](char a, char b){
				return a < b;
			});
			std::sort(s_even + 1, s_even + n/2 + 1, [&](char a, char b){
				return a < b;
			});
			for (int i = 1; i <= n/2; i++) ts1 += s_odd[i], ts1 += s_even[i];
			if (n&1) ans += s_odd[n/2+1];
			
			for (int i = n; i; --i) {
				int l = n - i + 1;
				if (l & 1) s_odd[(l+1)/2] = s[i];
				else s_even[l/2] = s[i];
			}
			
			std::sort(s_odd + 1, s_odd + (n+1)/2 + 1, [&](char a, char b){
				return a < b;
			});
			std::sort(s_even + 1, s_even + n/2 + 1, [&](char a, char b){
				return a < b;
			});
			for (int i = 1; i <= n/2; i++) ts2 += s_odd[i], ts2 += s_even[i];
			if (n&1) ans += s_odd[n/2+1];
			
			ans = std::min(ts1, ts2);
		}
		if (k < n) {
			std::sort(s + 1, s + n + 1, [&](char a, char b){
				return a < b;
			});
			for (int i = 1; i <= n; i++) ans += s[i];
		}
	}
	
	std::cout << ans << '\n';
}

int main() {
	int _ = 1; std::cin >> _;
	while ( _-- ) { solve(); }
	return 0;
}

C. 给一个数 \(x\) ,把它变成 \(1\) 。具体操作为每次可以让 \(x\) 减去一个它的因子,但是每个因子不能减去超过两次。

题目其实有引导性地让我们分两步做,每一步每个因子至多会用到一次。

方法:以二进制形式观察 \(x\) 。

  1. 第一步,让 \(x\) 每次减去最低位的 \(1\) 直到 \(x\) 只余一个 \(1\) 。
  2. 第二部,让 \(x\) 的 \(1\) 每次右移一位直到变为 \(\cdots 0001\) 。

第一步,显然二进制上任意一个 \(1\) 的权重不同,不会重复。

只需证: \(x\) 最低位的 \(1\) 一定是它的因子 。(此题主要考点)
证明:将二进制低位到高位编号,最低位为 \(0\) 。第 \(k\) 个 \(1\) 代表 \(2^{i_k}\)。显然有 \(2^{i_1} | 2^{i_1} + 2^{i_2} + \cdots + 2^{i_k}\) 。qed。

第二步,也显然余下的 \(x\) 只剩下一个 \(1\) 即 \(2^m\) 的形式,每次减去一半既是它的因子,也不重复。

两步分别用到的因子不重复,故总的用到的因子不会重复 \(2\) 次以上。

view
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
	int x; std::cin >> x;
	std::vector<int> ans(1, x);
	while ( __builtin_popcount(x) != 1 ) {
		x -= ( x & ( -x ) );
		ans.push_back(x);
	} 
	while (x != 1) {
		x >>= 1;
		ans.push_back(x);
	}
	int m = ans.size(); std::cout << m << '\n';
	for (int i = 0; i < m; i++) std::cout << ans[i] << " \n"[i==m-1];
}

int main() {
	int _ = 1; std::cin >> _;
	while ( _-- ) { solve(); }
	return 0;
}

D. 给一个 \(n \times n\) 的 \(01\) 网格。定义 \(a_{i, j}\) 的一次翻转为 \(a_{i, j} \oplus 1\) 。

对 \((i, j)\) 位置的格子翻转会产生以下影响:

  1. 自身翻转,即 \(a_{i, j} = a_{i, j} \oplus 1\) 。
  2. 任意 \((x, y)\) 位置满足 \(x > i\) 的格子翻转,满足 \(x - i \geq |y - j|\) 的格子翻转。

解方程:
不要被笛卡尔坐标系给荼毒了……。

\[\begin{cases} x > i \\ x - i \geq |y - j| \end{cases} \Rightarrow \begin{cases} x - y \geq i - j, \quad x > i, y \geq j \\ x + y \geq i + j, \quad x > i, y \leq j \end{cases} \]

在所给坐标系中是垂直向下九十度的扇形。

考虑:

  1. \(01\) 翻转最 \(naive\) 的性质:一个位置翻转 \(n\) 次,只有其中的 \(n\&1\) 次是有意义的。
  2. 网格内操作某个格子带动翻转一个区域且“翻转形式唯一或能转化为翻转形式唯一”的情形,具备一些性质:(只有区域翻转形式唯一情况下才能直接进行数理讨论,否则考虑 \(DP\) 等方案)
    1. 任何方向有后效性的翻转:当边界行(通常选第一行)的状态确定,整个方格的状态唯一确定。可以知晓有无解,有解时的最小操作次数。
      • 证明:当边界行状态确定,由于后效性存在,不存在一种方案可以维持已确定的边界行的状态且改变其他行。
      • 推论:当边界格的状态唯一确定,整行的状态唯一确定。
    2. 存在或可转化为一个方向无后效性的翻转:贪心地选择无后效性的方向翻转一定有解,当方向唯一时最小操作次数唯一。(比如此题的格子翻转严格影响向下的区域)
      • 证明:有解性显然。最优性可以反证按其他方向翻转不会更优。

于是此题变为,如何在 \(O(n^2)\) 或者 \(O(n^2 \log n)\) 的复杂度内,完成从第 \(1\) 行到第 \(n\) 行逐行操作所有 \(1\) 的格子。

  1. 仅按从上到下的方向操作所有 \(1\) 便是 \(O(n^2)\) 的,所以操作复杂度需要是 \(O(1)\) 或 \(O(\log n)\) 。
  2. 于是面临两个问题:
    • 空间允许情况下容易想到二维区间修改,很像需同时修改和询问。
    • 差分空间非水平,而是斜率为 \(1\) 和 \(-1\) 的空间。
      这调用 \(fenwik\ tree\) 将会是一套繁杂的推式子和代码实现。
  3. 向下斜率为正负 \(1\) 的区间修改问题,是经典的 \(tag\) 下放问题。
    • 我们只需要在 \(\mod 2\) 意义即位异或意义下,下放 \(tag\) 合并更新即可。
    • 三叉树中 \(tag\) 的下放比二叉树中 \(tag\) 的下放区别:
      • 二叉树的 \(tag\) 压标记时直接压到左右儿子即可,常见于二叉搜索树的 \(lazy\ tag\) 。
      • 三叉树的 \(tag\) 压标记时也只把标记压到左右儿子.若左右儿子都存在,则给中孙子放一个负标记(如果它存在)。更新信息时不仅要加上自己的标记,还要加上父亲的标记。
        (待补图)
      • 三叉树更常见于如网格中这种具备垂直关系的结构中应用。

时间复杂度 \(O(n^2)\) 。

view
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
	int n; std::cin >> n;
	std::vector<std::vector<int> > a(n+1, std::vector<int>(n+1)), tag(n+1, std::vector<int>(n+1));
	
	for (int i = 1; i <= n; i++) {
		std::string s; std::cin >> s;
		for (int j = 1; j <= n; j++) {
			a[i][j] = (s[j - 1] - '0');
		}
	}
	
	auto settag = [&](int i, int j, int v) {
		tag[i][j] = ( tag[i][j] + v ) % 2;
	};
	
	auto pushdown = [&](int i, int j) {
		if (i + 1 <= n) { // 下一层存在 
			a[i + 1][j] = ( a[i + 1][j] + tag[i][j] ) % 2; // 更新中儿子信息 
			if (j - 1 >= 1) { // 标记压到左儿子,更新左右子信息 
				tag[i + 1][j - 1] = ( tag[i + 1][j - 1] + tag[i][j] ) % 2;
				a[i + 1][j - 1] = ( a[i + 1][j - 1] + tag[i][j] ) % 2;
			}
			if (j + 1 <= n) { // 标记压到右儿子,更新右儿子信息 
				tag[i + 1][j + 1] = ( tag[i + 1][j + 1] + tag[i][j] ) % 2;
				a[i + 1][j + 1] = ( a[i + 1][j + 1] + tag[i][j] ) % 2;
			}
		}
		if (i + 2 <= n && j - 1 >= 1 && j + 1 <= n) { // 中孙子在存在且左右儿子都存在则打上负标记 
			tag[i + 2][j] = ( tag[i + 2][j] - tag[i][j] + 2 ) % 2; 
			a[i + 2][j] = ( a[i + 2][j] - tag[i][j] + 2 ) % 2;
		}
		tag[i][j] = 0; // 压完标记清空 
	};
	
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (a[i][j]) ans++, settag(i, j, 1);
			pushdown(i, j);
		}
	}
	
	std::cout << ans << '\n';
}
int main() {
	int _ = 1; std::cin >> _;
	while ( _-- ) { solve(); }
	return 0;
} 

标签:std,Space,int,Contest,long,tag,solve,Div,翻转
From: https://www.cnblogs.com/zsxuan/p/17660697.html

相关文章

  • AtCoder Beginner Contest 317 F - Nim
    数位DP#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;intdp[64][10][10][10][2][2][2][2][2][2];intmain(){lln;intb1,b2,b3;cin>>n>>b1>>b2>>b3;memset(dp,-1,sizeofdp);strings......
  • Harbour.Space Scholarship Contest 2023-2024 (Div. 1 + Div. 2)
    Preface因为不清空E题调了好久才过,没时间看后面的题了遂2h下机,赛后感觉F还是可做的这周三和周四的CF因为第二天有课可能都要开另一个小号随便打打了,毕竟有早八还打到两点钟实在是顶不住的说A.IncreasingandDecreasing从后往前贪心地确定每个数,最后检验下即可#include<cst......
  • java.lang.OutOfMemoryError: Java heap space 解决之道
    使用Java程序从数据库中查询大量的数据时出现异常:java.lang.OutOfMemoryError:Javaheapspace在JVM中如果98%的时间是用于GC且可用的Heapsize不足2%的时候将抛出此异常信息。JVM堆的设置是指java程序运行过程中JVM可以调配使用的内存空间的设置.JVM......
  • PermGen space简介
    PermGenspace简介 PermGenspace的全称是PermanentGenerationspace,是指内存的永久保存区域OutOfMemoryError:PermGenspace从表面上看就是内存益出,解决方法也一定是加大内存。说说为什么会内存益出:(1)这一部分用于存放Class和Meta的信息,Class在被Load的时候被放入PermGensp......
  • Codeforces Round 888 (Div. 3)G. Vlad and the Mountains(数据结构,图论)
    题目链接:https://codeforces.com/contest/1851/problem/G 大致题意: 给出n个点m条边的无向图,每个点有点权h【i】。从点i到点j会消耗h【j】-h【i】的能量,如果小于0,那么就是恢复对应绝对值的能量。 进行q次询问,每次询问包含起点s,终点t,能量e,能量在移动过程中不能小......
  • Codeforces Round 887 (Div. 1)C. Ina of the Mountain(数据结构,反悔贪心)
    题目链接:https://codeforces.com/problemset/problem/1852/C 题意: 给定一个长度为n的序列和正整数k; 每次可以选取任意一个区间,将区间内每个数减1; 如果出现一个数变成0,那么那个数变成k; 问至少操作多少次可以使得每个数变成k; 分析: 将每个数值抽象为对应高度的......
  • Codeforces Round 892 (Div. 2)E. Maximum Monogonosity(动态规划,数学)
    题目链接:https://codeforces.com/contest/1859/problem/E 题意: 有长度为n的a和b俩个序列,定义f【l,r】=abs(a【l】-b【r】)+abs(b【l】-a【r】); 给正整数k,求 不相交的区间且  所有  区间的长度 的 和 为k的最大值 是多少? 分析: 这里借鉴一个佬......
  • Educational Codeforces Round 151 (Rated for Div. 2)E. Boxes and Balls(数学,动态规
    题目链接:https://codeforces.com/contest/1845/problem/E 题意: 给定长度为n且只含0和1的数组,你可以进行以下操作: 交换相邻的0和1; 给正整数k,问经过k次操作后,会有多少种本质不同的结果; 分析: 如果1比0多,我们可以把他们取反(让0比1多,结果是一样的) 设计状态dp[i][j......
  • Codeforces Round 885 (Div. 2)E. Vika and Stone Skipping(数学,质因数分解)
    题目链接:https://codeforces.com/problemset/problem/1848/E 大致题意: 打水漂,某人在海岸线以f(正整数)的力量扔出石头,会在f,f+(f-1),f+(f-1)+(f-2),........,f+(f-1)+.....+2+1,的位置接触水面; 现在给出坐标x和q次询问,每次询问给出一个y值,将x=x*y;假设石头在x的位置接触水面,问有多少......
  • Codeforces Round 885 (Div. 2) F. Vika and Wiki(数学,倍增)
    题目链接:https://codeforces.com/problemset/problem/1848/F 大致题意:长度为n(n是2的幂次),每轮让a【i】=a【i】^a【i%n+1】,(^为异或)问需要操作多少次后可以使得每个数为0; 解题思路:我们来观察:第一次相当于:a【i】=a【i】^a【i+1】,a【i+1】=a【i+1】^a【i+2】第二次......