https://codeforces.com/contest/1370

C. Number Game

 

题意:给定一个数n,两个人轮流操作,每次操作可以n-1或n除以它的一个奇数因子。先变为1的人赢。

显然如果n是奇数或2,先手必胜。

如果n没有奇数因子,即n是除2以外的2的幂次,则先手必败。

如果n是2和一个奇质数的乘积,则先手必败。

所以,如果n不是2的幂次,且有多个2,则先手必胜。如果n只有1个2,且有多个奇质数,则先手必胜。

其他情况先手必败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int N = 2e5 + 10;
int T;
int prime[N];
bool is_prime[N];
int vis[N];
int euler_sieve(int n) {
int cnt = 0;
memset(vis, 0, sizeof(vis));
memset(prime, 0, sizeof(prime));
for (int i = 2; i <= n; i++) {
if (!vis[i]) { vis[i] = i; prime[cnt++] = i; is_prime[i] = true; }
for (int j = 0; j < cnt; j++) {
if (i*prime[j] > n) break;
vis[i*prime[j]] = prime[j];
if (i%prime[j] == 0)
break;
}
}
return cnt;
}
int main() {
int t = euler_sieve(100000);
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
if (n == 1)puts("FastestFinger");
else if (n == 2)puts("Ashishgup");
else if (n & 1)puts("Ashishgup");
else {
int cnt = 0, cnt2 = 0;
for (int i = 0; 1ll*prime[i]*prime[i] <= n; i++) {
if (n%prime[i] == 0) {
while (n%prime[i] == 0) {
if (prime[i] & 1)cnt++;
else cnt2++;
n /= prime[i];
}
}
}
if (n != 1)cnt++;
if (cnt == 0)puts("FastestFinger");
else {
if (cnt2 > 1)puts("Ashishgup");
else {
if (cnt>1)puts("Ashishgup");
else puts("FastestFinger");
}
}
}
}
return 0;
}

 

D. Odd-Even Subsequence

 

题意:给定一个数组,要求找出一个长度为k的子序列,要求最小化子序列奇数位置和偶数位置分别的最大值的最小值。

二分+dp

一个数可以作为答案,说明存在一个子序列,满足奇数位置上的所有数都小于等于它,或者偶数位置上的所有数都小于等于它。

如果存在 k/2\lceil k/2\rceil 个不相邻的数满足都小于等于x,则一定可以构造出这样一个序列。

可以dp求出最多的不相邻的小于等于x的数。

而如果只有 k/2\lfloor k/2\rfloor 个,那么还要考虑如果在两边,那么可能无法构造出,所以要把两边都排除之后再求一次。

奇偶不同情况也要考虑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int N = 2e5 + 10;
int n, k;
int a[N], dp[N][2];
bool ck(int x) {
dp[0][0] = dp[0][1] = 0;
for (int i = 1; i <= n; i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = dp[i - 1][0] + (a[i] <= x);
}
int t = max(dp[n][0], dp[n][1]);
if (k & 1) {
if (t >= (k + 1) / 2)return true;
if (t < k / 2)return false;
int p = a[1], q = a[n];
a[1] = a[n] = 2 * INF;
dp[0][0] = dp[0][1] = 0;
for (int i = 1; i <= n; i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = dp[i - 1][0] + (a[i] <= x);
}
t = max(dp[n][0], dp[n][1]);
a[1] = p, a[n] = q;
if (t >= k / 2)return true;
return false;
}
else {
if (t >= k / 2 + 1)return true;
if (t < k / 2)return false;
int p = a[1]; a[1] = INF;
dp[0][0] = dp[0][1] = 0;
for (int i = 1; i <= n; i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = dp[i - 1][0] + (a[i] <= x);
}
t = max(dp[n][0], dp[n][1]);
a[1] = p;
if (t >= k / 2)return true;
p = a[n]; a[n] = INF;
dp[0][0] = dp[0][1] = 0;
for (int i = 1; i <= n; i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = dp[i - 1][0] + (a[i] <= x);
}
t = max(dp[n][0], dp[n][1]);
a[n] = p;
if (t >= k / 2)return true;
return false;
}
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
ck(1);
int L = 1, R = INF;
while (L < R) {
int mid = (L + R) / 2;
if (ck(mid))R = mid;
else L = mid + 1;
}
printf("%d\n", L);
return 0;
}

 

E. Binary Subsequence Rotation

 

题意:给定两个等长的01串,每次操作对第一个串选一个子序列,环形向右平移一个字符,问最少多少次操作后变成第二个串。

贪心+模拟

显然对应相等的位置可以不用动了,所以只考虑不等的。

每次都是移动01010101这样的序列,因为像10001这样的,移动后实际上等同于只移动了最右边的01。

然后就是贪心选择最长的0101或101010串,最终有几个这样的串,就要操作几次。

但是实际上对于01010这样首和尾相同的串,平移一次后并不能使所有位置都对齐,而是会剩下一位。但是这样并不会影响结果,因此对于每个01010,一定会有一个10101对应,还是可以通过两次操作把这两个串都对齐。

实现方式还是要注意的,遍历每一位,同时记录是否有以0结尾或1结尾的0101串,如果当前位是0,则接在1后,并使1串变为0串,如果没有1串,则新建一个0串。如果当前位为1也类似。至于不用考虑010这样的串的原因就是上一段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int N = 1e6 + 10;
int n;
char s[N], t[N];
int cn[2];
int c0, c1;
int main() {
scanf("%d", &n);
scanf("%s%s", s + 1, t + 1);
for (int i = 1; i <= n; i++) {
if (s[i] == t[i])continue;
if (s[i] == '0') {
if (c1 > 0)c1--;
c0++;
cn[0]++;
}
else {
if (c0 > 0)c0--;
c1++;
cn[1]++;
}
}
if (cn[0] != cn[1])puts("-1");
else printf("%d\n", c0 + c1);
return 0;
}