Codeforces Round 884 (Div. 1 + Div. 2)
A. Subtraction Game
题意:
给出两个整数a,b,两个人玩游戏,每次必须从一堆石头中去掉a或b个,无法完成此操作的人输。要求一个n代表石头的总数,使得第二个人一定能赢得比赛。
Idea:
n == a + b 时第二个人一定获胜
B. Permutations & Primes
题意:
求一个长度为n的排列,在所有等长排列中,该排列能找到最多的[l,r]子串,这些子串中最小的未出现的正整数是一个素数
Idea:
想象双指针枚举l,r的过程,每当r指正左移或l指正右移的时候,我们希望能使新子串的MEX为素数,所以直接将最小的两个素数放在排列的两端,指针移动过程中就会有很多子串的MEX都是这些小的素数,且因为1不是素数,1还是最小的正整数,所以把1放在整个排列中间的位置,让尽可能多的子串包含1,降低它成为MEX的可能性。
所以可以进一步推出,只要将越小的素数放在越靠排列两端的位置,越小的非素数都放在排列中间的位置就是答案。
code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int a[N];
bool isprime[N];
int prime[N]; // 现在已经筛出的素数列表
int n;
int cnt;
void euler()
{
memset(isprime, true, sizeof(isprime));
isprime[1] = false;
for (int i = 2; i <= n; ++i)
{
if (isprime[i])
prime[++cnt] = i;
for (int j = 1; j <= cnt && i * prime[j] <= n; ++j)
{
isprime[i * prime[j]] = false;
if (i % prime[j] == 0)
break;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
n = N;
euler(); //欧拉筛预先筛出所有需要的素数
int t;
cin >> t;
while (t--)
{
cin >> n;
int q = 1, p = n;
for (int i = 1; i <= n; i++)
if (isprime[i])
{
if (i & 1)
a[p--] = i;
else
a[q++] = i;
}
for (int i = n; i >= 1; i--)
if (!isprime[i])
if (i & 1)
a[p--] = i;
else
a[q++] = i;
for (int i = 1; i <= n; i++)
cout << a[i] << ' ';
cout << '\n';
}
return 0;
}
C. Particles
题意:
给出一个长度为n,由整数组成的数组,删掉其中的某个数时,若产生的空隙两端有数,这两个数就会合并为一个数,为两数之和。问执行n-1次操作,最后的到的一个数最大是多少
Idea:
一开始我考虑dp[i]为长度为i的前缀的最大能保留值,状态转移方程为
d
p
[
i
]
=
m
a
x
(
a
[
i
]
,
d
p
[
i
−
2
]
+
a
[
i
]
)
dp[i] = max(a[i], dp[i-2] + a[i])
dp[i]=max(a[i],dp[i−2]+a[i])
然后wa了。
后来想到dp[i]不止可以与dp[i-2]的数字合并,其实当i为奇数时,dp[i]可以和前面所有奇数位的dp[i]合并,为偶数时同理。所以我在原本的基础上分别维护当前位置之前的奇数位置和偶数位置的最大dp[i]值,从该值合并,修改转移方程就过了:
d
p
[
i
]
=
m
a
x
(
a
[
i
]
,
l
a
s
t
[
i
&
1
]
+
a
[
i
]
)
;
dp[i] = max(a[i], last[i\&1] +a[i]);
dp[i]=max(a[i],last[i&1]+a[i]);
赛后看题解发现其实不用dp,直接取所有奇数位和偶数位的正数分别相加再取最值即可,当然要全是负数时直接取最大数
dp code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int a[N], n, dp[N], last[2];
signed main()
{
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], dp[i] = a[i];
int ans = a[1];
dp[0] = -INT_MAX;
last[0] = last[1] = 0;
last[1] = max(last[1], a[1]);
for (int i = 2; i <= n; i++)
{
dp[i] = max(dp[i], last[i & 1] + a[i]);
last[i & 1] = max(last[i & 1], dp[i]);
ans = max(ans, dp[i]);
}
cout << ans << '\n';
}
return 0;
}
贪心 code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10;
int a[N], n;
signed main()
{
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
int t;
cin >> t;
while (t--)
{
cin >> n;
int mx = -INT_MAX;
for (int i = 1; i <= n; i++)
cin >> a[i], mx = max(mx, a[i]);
if (mx <= 0)
{
cout << mx << '\n';
continue;
}
int ans[2] = {0};
for (int i = 1; i <= n; i++)
ans[i & 1] += max(0ll, a[i]);
cout << max(ans[0], ans[1]) << '\n';
}
return 0;
}
D. Row Major
题意:
求一个长度为n的字符串,全部使用小写字母且使用字母的种类要尽量少,还要满足当这个字符串按顺序排成任意可能大小的矩形时相邻的字符不能相同(比如[1,2,3,4,5,6]可以排成一个二行三列的矩形,第一行[1,2,3]第二行[4,5,6])
Idea:
找到一个最小的不能将n整除的数k,最少能使用的字母数就是k,循环将k个字母填入字符串,因为无法排成k列的矩阵,所以相同的字母永远不会相邻
code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int n, k;
void solve()
{
cin >> n;
k = n;
for (int i = 2; i < n; i++)
if (n % i)
{
k = i;
break;
}
int c = 0;
for (int i = 1; i <= n; i++)
{
cout << (char)('a' + c++);
c %= k;
}
cout << '\n';
}
signed main()
{
int t;
cin >> t;
while (t--)
solve();
return 0;
}