GXNU竞赛集训队第一次训练题解

2022/3/19训练题单

1352B
1512D
1368B
1542B
1399D
1372C
1400C
1352F
282C
916C
980B
214B

B. Same Parity Summands

题意: 将整数 n n n 拆分成 k k k 个 奇偶性相同的数的和, 换句话来说就是将 n n n 拆分成 k k k 个奇数 或者 k k k 个偶数, 如果存在,则将这 k k k 个奇数,或者 k k k 个偶数打印出来, 不存在则输出"NO"

假设拆分成奇数, 无论拆分成多大的奇数, 这些奇数是一定可以拆分成 1 1 1 的,我们不妨先将 拆分出 k − 1 k - 1 k1 1 1 1, 然后再判断 n − ( k − 1 ) n - (k - 1) n(k1) 是否为奇数, 如果不为奇数, 则这种情况不存在

假设拆分成偶数,无论拆分成多大的偶数, 这些偶数是一定可以拆分成2的,我们不妨先拆分出 k − 1 k-1 k1 2 2 2, 注意, 这里还需要判断一下, n 是否大于 2 × ( k − 1 ) 2 × (k - 1) 2×(k1), 判断完后, 再判断 n − 2 × ( k − 1 ) n - 2 × (k - 1) n2×(k1) 是否为偶数, 如果不为偶数, 则这种情况不存在

如果上述两种情况都不满足, 则输出无解

代码:

#include<bits/stdc++.h>

using namespace std;

int t, n, k;

void solve()
{

	cin >> n >> k;

		if(n > k - 1 && (n - (k - 1)) % 2 != 0) //全是奇数
		{
			cout << "YES\n";
			for(int i = 1; i < k; i ++ )
				cout << 1 << " ";
			if(n - (k - 1) > 0)
			cout << n - (k - 1) << '\n';
		return;
		}
		else if(n > 2 * (k - 1) && (n - 2 * (k - 1) ) % 2 ==  0)  //全是偶数
		{
			cout << "YES\n";
			for(int i = 1; i < k; i ++ )
				cout << 2 << " ";
			if(n - (k - 1) * 2 > 0)
			cout << n - (k - 1) * 2<< '\n';
			return;
		}
		cout << "NO\n";
}
int main()
{
	cin >> t;
	while(t -- ) 
		solve();
}

D. Corrupted Array

题目大意: 给你一个序列 B B B, 一共有 n + 2 n+2 n+2 项, 其中有 n n n项是另一个数列, 还有一项是另一个数列的前 n n n 项和, 还一项是 X X X

你可以将 X X X 那一项就把它当做,删去某一项, 因为 X X X 没有作用, 然后剩下的就是 n n n 项的数列, 和一个前 n n n 项和, 我们不难发现 前 n n n 项和 一定是最大的,或者第二大的,

不存在这种情况则输出无解

我们设最大的项为 a n + 2 a_{n + 2} an+2, 第二大的为 a n + 1 a_{n + 1} an+1
所以我们可以从先往后枚举删去每一项, 如果当前 n n n 项和为 a n + 1 a_{n + 1} an+1时, 那么我们要将整个序列的总值减去 a n + 1 a_{n + 1} an+1 的,减去X判断是否等于 a n + 1 a_{n + 1} an+1
如果当前 n n n 项和为 a n + 2 a_{n + 2} an+2 时, 那么我们要将整个序列的总值减去 a n + 2 a_{n + 2} an+2, 减去 X X X 判断是否等于 a n + 2 a_{n + 2} an+2
并且删除的项与前 n n n 项和不能是同一项

#include<bits/stdc++.h>

using namespace std;
#define int long long 
const int N = 2e5 + 10;
int t, n, s[N], a[N];

void solve()
{
	cin >> n;
    memset(s,0,sizeof s);
	for(int i = 1; i <= n + 2; i ++ ) cin >> a[i], s[i] = s[i - 1] + a[i];  //预处理前缀和

	sort(a + 1, a + 3 + n );  //排序

	for(int i = 1; i <= n + 2; i ++ )
	{
			//当第 n + 1 项为前 n 项和
			//总值 - X - a[n + 1] == a[n + 1] && 删去的不是第 n + 1 项
		if((s[n + 2] - a[i] == 2 * a[n + 1]) && i != n + 1)   
		{
			for(int j = 1; j <= n + 2; j ++ )
			{
				if(j != i && j != n + 1) //删去的那项与 第n + 1 项不用打印
					cout << a[j] << " ";
				//	cout << j << '\n';
			}
			cout << '\n';
				return;	
		}
		 //当第 n + 2 项为前 n 项和
			//总值 - X - a[n + 2] == a[n + 2] && 删去的不是第 n + 2 项
		if((s[n + 2] - a[i] == 2 * a[n + 2]) && i != n + 2)
		{
				for(int j = 1; j <= n + 2; j ++ )
			{
				if(j != i && j != n + 2)  //删去的那项与 第n + 2 项不用打印
			 	cout << a[j] << " ";
			}
			cout << '\n';
			return;
		}
	}

	cout << "-1\n";
	return;
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin >> t;
	while(t -- ) 
		solve();
}

B. Codeforces Subsequences

题目大意: 给出单词"Codeforces"的数量,请你打印出能够组成该数量的字符串

这里要注意Codeforces 中的不同位置的相同字母不属于同一个字母, "Codeforces"的数量就等于每个位置字母出现次数的乘积(组合数学的知识)
初始状态是每个字母出现次数都为 1 1 1, 然后依次给每个字母的出现次数增加, 当大于等于 n n n 时, 就退出循环

#include<bits/stdc++.h>

using namespace std;
#define int long long 
const int N = 2e5 + 10;
int t, n;

int cnt[20];
char s[N] = {"codeforces"};
void solve()
{
	cin >> n;

	for(int i = 0; i < 10; i ++ ) cnt[i] = 1;  //初始化为1

	int num = 1, idx = 0;

	while(1)
	{
	    num = 1;
		for(int i = 0; i < 10; i ++ )  //记录当前Codeforces的数量
			num *= cnt[i];
		if(num >= n) break;  //到达要求就退出

		cnt[idx] ++;  //依次给字母增加数量
		idx ++;   //移到下一个字母的位置
		if(idx == 10) idx = 0;  //到达最右边后,再回到最左边
	}
	
	for(int i = 0; i < 10; i ++ ) //依次打印每个字母
		for(int j = 1; j <= cnt[i]; j ++ )
			cout << s[i];

		cout << '\n';
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		solve();
}

B. Plus and Multiply

题目大意: 初始时数列中只有元素 1 1 1, 给定 a a a b b b, 每次操作,可以将 a × x , b + x a × x, b + x a×x,b+x 加入序列, x x x 为数列中的任意一个元素

不难发现 任意一个元素都一定由 a x + b × y a^x + b × y ax+b×y 组成, 所以我们去枚举 x x x ,判断 n − a x n - a^x nax 是否能被 b b b 整除, 一定不要去枚举 y y y, 会 t l e tle tle 的, 枚举 x x x 才行, 因为是指数型增长

#include<bits/stdc++.h>

using namespace std;
#define int long long 
const int N = 2e5 + 10;
int t, n;

void solve()
{
	 int n, a, b;
	 // a ^ x + by == m
	 // (m - a ^ x) % b == 0
	 cin >> n >> a >> b;
	     bool flag = false;
	     for(int i = 1; i <= n; i *= a)
	     {
	         if((n - i) % b == 0)
	         flag = true;
	         if(a == 1)
	         break;
	     }
	     if(flag) cout << "YES\n";
	     else cout <<"NO\n";
}
signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	cin >> t;
	while(t--)
		solve();
}

注意这里要特判 1 1 1 , 不然就死循环了

D. Binary String To Subsequences

题目大意: 将一个 01 01 01 s s s 分割为 不存在连续的 0 0 0 或者连续的 1 1 1 的子序列, 然后打印 01 01 01 串中分别属于那个子序列

分析: 我们可以维护一个 01 01 01 队列, 每次操作后将其放入队列, 当元素为 1 1 1 时,判断 1 1 1 队列 是否为空 , 为空则子序列数量 + 1 +1 +1, 不为空则将其与第一个 1 1 1 匹配,并且将该元素加入 0 0 0 队列, 当元素值为 0 0 0 时也同理可知

#include<bits/stdc++.h>

using namespace std;
#define int long long 
const int N = 2e5 + 10;
int t, n;
char s[N];
void solve()
{
	cin >> n;
	queue<int>q[2];
	vector<int>ans;
	for(int i = 1; i <= n; i ++ )
		cin >> s[i];
	int cnt = 1;
	for(int i = 1; i <= n; i ++ )
	{
		int c = s[i] - '0';
		if(c == 0)
		{
			if(q[1].empty())ans.push_back(cnt++); //如果1为空 答案++
			else ans.push_back(ans[q[1].front()]),q[1].pop();  // 将0与第1个1匹配, 然后将这个1出队
			q[0].push(i - 1);   //将0放进队列
		}
		else {
			if(q[0].empty())ans.push_back(cnt++);  //如果0为空 答案++
			else ans.push_back(ans[q[0].front()]), q[0].pop(); //将1与第1个0匹配, 然后将这个0 出队
			q[1].push(i - 1); //入队
		}
	}
	cout << cnt - 1 << '\n';
	for(int i = 0; i < n; i ++ )
		cout << ans[i] << " ";
	cout << '\n';


}
signed main()
{
    int t;
    cin >> t;
    while(t -- ) solve();
}

C. Omkar and Baseball

题目大意: 给定一个 1 1 1 ~ n n n的排列, 被打乱了顺序, 然后你可以通过一种操作, 这种操作就是在一定范围给这些排列交换位置, 并且要保证这些范围内的数的位置一定要发生改变, 请问多少次操作才能使得排列还原

首先我们可以先从最左边开始, 已经还原好了的,并且是连续的给他删去,因为这些是不用操作的, 然后再从最右边开始, 已经还原好了的, 并且是连续的给他删去, 然后在剩下的序列中扫一遍看有没有元素已经被还原了,如果有,我们需要进行一次操作,让他们的位置全部错开,都不在自己应该待的位置, 因为我们进行一次操作必须让每一个元素的位置发生改变,如果有元素已经被还原了,那么操作过后,它将会变成未被还原的状态

#include<bits/stdc++.h>

using namespace std;
#define int long long 
const int N = 2e5 + 10;
int t, n;
int a[N];
void solve()
{
	cin >> n;

	for(int i = 1; i <= n; i ++ ) cin >> a[i];

        bool flag = 1;  //记录是否有被打乱的数
		int bg = 1, ed = n;
	for(int i = 1; i <= n; i ++ )
		if(a[i] != i ) //如果这一位被打乱了,说明,左边的都是已经被还原好并且连续
		{
		    flag = 0;  //记录一下,数列存在元素被打乱
			bg = i > 1 ?  i - 1 : 1; 
			break;
		}
    
	for(int i = n; i >= 1; i -- )
		if(a[i] != i) //如果这一位被打乱了,说明,右边的都是已经被还原好并且连续
		{ 
		    flag = 0;   //有被打乱的数
		    ed = i > 1 ? i + 1 : 1;
	     	break;
		}
	if(flag)   //如果没有被打乱的数, 说明序列本身就已经被还原了
    {
        cout << "0\n";
        return;
    }
	int res = 0;
		for(int i = bg + 1; i <= ed - 1; i ++ ) //从被打乱的位置开始
		    if(a[i] == i)  //存在已经被还原的数
		    	{
		    	res = 1;
		    	break;
		 		}
		 if(res == 1) cout << 2 << '\n';  //先打乱,然后全部还原
		 else cout << 1 << '\n';  //直接还原
}
signed main()
{
	 int t;
	 cin >> t;
	 while(t -- )
	 	solve();
}

C. Binary String Reconstruction

题目大意: 给定一个字符串 s s s,;
对于字符串中第 i i i 个字符, 如果 i > x i > x i>x, 并且 w i − x = 1 w_{i-x} = 1 wix=1, 那么 s i = 1 s_i = 1 si=1
对于字符串中第 i i i 个字符, 如果 i + x ≤ n i + x ≤ n i+xn, 并且 w i + x = 1 w_{i+x} = 1 wi+x=1, 那么 s i = 1 s_i = 1 si=1
如果不存在上述两种情况,则 s i = 0 s_i = 0 si=0

我们可以先将 w w w 全部设成 1 1 1, 然后根据字符串 s s s的要求将其元素设置为 0 0 0, 然后再判断符合题意, 即 s i = 1 s_i = 1 si=1时,是否符合题意

#include<bits/stdc++.h>

using namespace std;
#define int long long 
const int N = 2e5 + 10;
int t, n;
int w[N];
void solve()
{
	 string s;
	 int x;
	 cin >> s;
	 cin >> x;
	 for(int i = 0; i < (int)s.size(); i ++ )  //全部初始化为1
	 	w[i] = 1;

	 for(int i = 0; i < (int)s.size(); i ++ )  //扫一遍,处理0
	 	if(s[i] == '0')
	 	{
	 		if(i - x >= 0 ) w[i - x] = 0;  //i - x 存在 设置为0 
	 		if(i + x < (int)s.size() ) w[i + x] = 0; //i + x 存在, 设置为 1 
		}
	for(int i = 0; i < (int)s.size(); i ++ )
	{
		bool flag1 = false;  //记录是否有解
		if(s[i] == '0') continue;  //0 是已经处理好了的,不用管
		if(i - x >= 0 && w[i - x] == 1) flag1 = true;  //标记有解
		if(i + x < ((int)s.size()) && w[i + x ] == 1) flag1 = true;//标记有解
		if(!flag1)  //如果无解打印-1 然后退出
		{
		 cout << -1 << '\n';
		 return;
		}
	}
		for(int i = 0; i < (int)s.size(); i ++ ) cout << w[i];
			cout << '\n';
}
signed main()
{
	 int t;
	 cin >> t;
	 while(t -- )
	 	solve();
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广西小蒟蒻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值