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

1864A - Increasing and Decreasing 

        题意:给定x,y,n,其中要求构造一个数组a,其中满足a有n个元素,且起始元素为x,末尾元素为y,且a是一个严格单增的数组。同时满足数组b , 其中b_{i} = a_{i + 1} - a_{i} , 要求b是严格单调递减的。

        思路:直接从b开始构造,假设b的最后一个元素是1,往前依次递增1,求该情况下a_{1}是多少,若a_{1} < x , 则代表无法满足。反之则能够满足。直接将a_{1}改为x。其余不变即可。

        

void solve() 
{
	int x , y , n;
	cin>>x>>y>>n;
	vectorans;
	ans.pb(y);
	for(int i = 1 ; i < n ; i ++){
		y -= i ;
		ans.pb(y);
	}
	if(ans[ans.size() - 1] >= x){
		cout<= 0 ; i --){
			cout<<" "<

 1864B - Swap and Reverse 

        题意:给定字符串s,一个整数k,其中能够进行两种操作。

1、交换s_{i} \ s_{i + 2}

2、翻转[i , i + k - 1]内的所有字符。

        假设能进行无数次这两种操作,求最终字符串的字典序最小串。

        思路:对于操作1而言,我们可以理解为奇数位排序和偶数位排序。如果说 k 奇数。那么翻转过后位于奇数位置上的字符仍然位于奇数,位于偶数上的字符仍然位于偶数。则只需要将奇数位偶数位分开排序即可。如果k是偶数,假如有字符串12345, k = 4 。 我们交换[1,4]之后变成了 43215 ,再一次交换[2,5]之后变成了45123。 这样就仅仅是4、5两个元素完成了奇偶对换。因此可以得出,只需要交换[i , i + k -1 ] 和 [i + 1 , i + k],就可以满足将任意一个奇数位上的数换成偶数位、偶数位上的数换成奇数位。最后在通过操作1,则完成了整体的排序。

        

void solve() 
{
	int n , k;
	cin>>n>>k;
	string s;
	cin>>s;
	if(k % 2 == 0){
		int a[n];
		for(int i = 0 ; i < n ; i++){
			a[i] = s[i] - 'a';
		}
		sort(a , a + n , cmp);
		char c;
		for(int i = 0 ;i < n ; i++){
			c = a[i] + 'a';
			cout<a , b;
		for(int i = 0 ; i < n ; i ++){
			if(i % 2 == 0){
				a.pb(s[i] - 'a');
			}
			else{
				b.pb(s[i] - 'a');
			}
		}
		sort(a.begin() , a.end() ,cmp);
		sort(b.begin() , b.end() ,cmp);
		char c;
		int i = 0 , j = 0;
		while(n){
			c = a[i++] + 'a';
			cout<

1864C - Divisor Chain  

        题意:给定数字a,每次操作将a减去a的其中一个因子。最终要求在1000次以内将a变为1,其中每个数最多能减两次。

        思路:遇事不决二进制。考虑到二进制数 10010 ,他的因子必然包含了 2^1 , 那么减去之后就变成了 10000 , 之后如何变成 00001 呢 ?考虑到二进制的除法。 10000 的因子必然有2^3 , 减去之后变成了 1000 , 以此类推,每次都将原数字除以二,最终就能变成1。 共有两轮操作,每轮中每位数最多操作1次,因此满足了每个数最多减2次。

        

void solve() 
{
	int n;
	cin>>n;
	int k = n;
	vectore;
	int cnt1 = 0;
	while(k){
		e.pb(k % 2);
		k /= 2;
	}
	vectorans;
	ans.pb(n);
	for(int i = 0 ; i < e.size() ; i ++){
		int d = i == 0 ? 1 : pow(2 , i);
		if(e[i] == 1){
			if(n > d){
				n -= d;
				ans.pb(n);
			}
			else{
				break;
			}
		}
	}
	while(n > 1){
		n/=2;
		ans.pb(n);
	}
	cout<

 1864D - Matrix Cascade 

        题意:给定一个n * n 的01网格,能够进行以下操作:选择一个[x,y] 的格子,将其自身和所有i > x \&\& i - x \geq \left | y - j \right | 的[ i , j ] 格子进行翻转。求将所有格子都变成0的最小操作数。

        思路:可以想到,第一个含有1的行中,其 1 必然只能通过自己进行翻转。因此每一行翻转过后,继续找到第一个含有1的行进行翻转,这样子操作的话能保证操作数最小。

        n * n 为 9e6 量级的数,因此每次翻转一个格子,将其下面的所有格子都翻转是不可能实现的。因此想到了线段树中的类似于懒标记的做法 : 记录一下翻转数在一行中的起点和终点。下放的过程当中,起点向左移动一格,终点向右移动一格。然后利用差分数组来求出每一个格子之前到底翻转了几次。再来判断是否需要翻转。

        

void solve() 
{
	int n;
	cin>>n;
	LL st[n];
	LL en[n + 5];
	LL a[n][n];
	memset(st, 0 , sizeof st);
	memset(en, 0 , sizeof en);
	for(int i = 0 ; i < n ; i++){
		string s;
		cin>>s;
		for(int j = 0; j < n ; j ++)
			a[i][j] = s[j] - '0';
	}
	LL ans = 0;
	for(int i = 0 ; i < n ; i ++){
		LL cnt = 0;
		vectortmp;
		st[0] += st[1];
		for(int j = 1 ; j < n - 1; j ++){
			st[j] = st[j + 1];
		}
		st[n - 1] = 0;
		for(int j = n ; j > 0 ; j --){
			en[j] = en[j - 1];
		}
		for(int j = 0 ; j < n ; j ++){
			cnt += st[j] - en[j];
			if((cnt + a[i][j]) % 2 == 1){
				tmp.pb(j);
				ans++;
			}
		}
		for(auto x : tmp){
			st[x] ++;
			en[x + 1]++;
		}
	}
	cout<

  1864E - Guess Game

        题意:有一个含有n个元素的数组,Alice 和 Bob 又在一起玩游戏了。其中Carol 作为裁判,每一次游戏会从数组中任意选择一个数a告诉Alice ,再选择一个数b告诉Bob , 再将 a | b 的结果告诉两人。其中Alice先玩,他需要说知道还是不知道,如果说知道,则需要给出 a 和 b 的大小关系,若不知道则轮到对方。求最优情况之下游戏轮数的期望。

        思路:(我自己想的,不一定正确)假如说a = 10010 , b = 11010 , a | b = 11010 ,那么Alice会首先说不知道,因为她无法确定对方的第一位是否为1。然后轮到Bob了,此时他知道:如果Alice第一位是0,那么他必然会说a < b 。 然而他没有,因此Alice的第一位是1。然后又因为自己第二位是1,但是无法确定对方第二位是多少,因此他也会放弃。到了第三轮,Alice看透了Bob的想法,因此她也知道了Bob的前两个数是11 ,而他是10,所以他会直接说a < b。综上:每一轮假如对方说不知道,那么对方的这一位数一定是1,同时a | b 的这一位也是1。假如说 a | b = 10111011,那么第一轮过后,Bob / Alice已知Alice是10xxx0xx , 第二轮过后,Alice / Bob已知Bob是101xx0xx,以此类推。涉及到前缀的比较,应该是用Trie树来实现?

你可能感兴趣的:(数据结构,c++,算法)