2022/3/19训练题单
1352B
1512D
1368B
1542B
1399D
1372C
1400C
1352F
282C
916C
980B
214B
题意: 将整数 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 k−1 个 1 1 1, 然后再判断 n − ( k − 1 ) n - (k - 1) n−(k−1) 是否为奇数, 如果不为奇数, 则这种情况不存在
假设拆分成偶数,无论拆分成多大的偶数, 这些偶数是一定可以拆分成2的,我们不妨先拆分出 k − 1 k-1 k−1 个 2 2 2, 注意, 这里还需要判断一下, n 是否大于 2 × ( k − 1 ) 2 × (k - 1) 2×(k−1), 判断完后, 再判断 n − 2 × ( k − 1 ) n - 2 × (k - 1) n−2×(k−1) 是否为偶数, 如果不为偶数, 则这种情况不存在
如果上述两种情况都不满足, 则输出无解
代码:
#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();
}
题目大意: 给你一个序列 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();
}
题目大意: 给出单词"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();
}
题目大意: 初始时数列中只有元素 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 n−ax 是否能被 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();
}
题目大意: 给定一个 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
wi−x=1, 那么
s
i
=
1
s_i = 1
si=1
对于字符串中第
i
i
i 个字符, 如果
i
+
x
≤
n
i + x ≤ n
i+x≤n, 并且
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();
}