https://codeforces.com/contest/2028

A. Alice’s Adventures in “Chess”

题意

有一个无限大的地图,地图的原点有一个机器人, 即他起初在坐标$(0,0)$处,并且会不断重复一段指令,指令的长度为$n$,指令只包含NESW字符,表示像对应的方向移动。

问机器人能否在某时刻到达坐标$(a,b)$处。

需要注意的是$1\le n,a,b \le 10$

思路

我们固然可以通过处理出一次循环所到达的位置,然后通过每次循环的偏移量来计算每个位置之后是否有可能到达目标。但是这样子虽然时间复杂度很好,但是程序写起来很麻烦。

我们注意到题目中给出的数据很小,因为机器人执行一套指令后,会移动固定的偏移量,于是在经过100轮移动后, 机器人对于$x$ 轴(或y轴)上,只有可能移动了超过100格,或者原地不动。在此之后一定不可能到达$a$

我们直接暴力执行100套操作,一定会将所有可能到达(a,b)的状态都遇到。

代码

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
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n,a,b;
cin>>n>>a>>b;
string s;
cin>>s;
int x = 0;int y = 0;
for(int i = 1;i<=100;i++){
for(auto ch : s){
if(ch == 'N') y++;
if(ch == 'S') y--;
if(ch == 'E') x++;
if(ch == 'W') x--;
if(x == a && y == b){
cout<<"YES\n";return ;
}
}
}
cout<<"NO\n";
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

B. Alice’s Adventures in Permuting

题意

有一个长度为$n$的数组$a$,由三个参数$n,b,c$决定 ,表示为$a_i = b * (i-1) + c$ 。

每进行一次操作,会将a的最大值变为$mex(a)$

问在进行几次后,a数组会变为$[0,1,2,3,…,n-2,n-1]$ 。

若无法达到目标,输出$-1$ 。

思路

本题是讨论题,容易发现,在一般情况下,a数组需要的操作数,即为a数组中大于等于n的元素的数量。

下面列举特殊情况:

  • 当$b = 0$ 时,a数组初始为$n$个$c$ ,此时
    • 若$c == n-1 \ or \ c == n- 2$ ,那么需要操作$n-1$次。
    • 若$c \ge n$ 那么需要操作$n$次
    • 若$c < n - 2$ ,那么无论如何都无法使得a变为目标数组,输出$-1$.
  • 当b不等于0时
    • 如果$c \ge n$ ,那么需要n次操作
    • 如果$c < n$ ,那么需要$n - \frac{n-1-c}{b} - 1$

代码

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
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
#define endl '\n'
void solve(){
int n,b,c;
cin>>n>>b>>c;
if(b == 0){
if(c == n - 1 || c == n - 2){
cout<<n - 1<<endl;
}else if(c >= n) cout<<n<<endl;
else cout<<-1<<endl;
return ;
}else if(c >= n){
cout<<n<<endl;
}else {
int t = (n-1-c)/b;t++;
cout<<n-t<<endl;
}
}
signed main(){
IO;
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

C. Alice’s Adventures in Cutting Cake

题意

给你一个长度为$n$的数组$a$ , 你需要将数组分为$m+1$个连续的部分,并且要求如下:

  1. 至少有m个子数组满足:子数组的和大于等于$v$
  2. 另外那个数组的子数组的和尽可能大。

问条件2中描述的子数组,和最大是多少?

思路

假设所有的数组都需要满足“子数组的和大于等于$v$” , 那么显然我们不断地添加元素,当元素的和大于等于v时,切分数组,就可以尽可能地满足条件。

但是现在其中有一个数组是可以不满足该条件的,那么将该数组分为$m+1$个子数组后,这m个子数组会被第$m+1$个子数组分开为两部分。

于是我们可以通过预处理数组的前后缀关系(例如前缀$pre[i]$ 表示为从$a_1$到$a_i$ 之间可以分成多少个大于等于$v$ 的数组)

于是我们枚举第$m+1$的数组的左端点$l$ ,在他左边有$pre[l-1]$个的子数组,于是我们就还需要$m - x $个子数组,二分寻找lst中的该值既可找到右端点$r$ 。

最终的答案就是$sum[r] - sum[l-1]$ 的最大值。

代码

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
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n,m,v;
cin>>n>>m>>v;
vector<int> a(n+5);
vector<int> pre(n+5);
vector<int> lst(n+5);
vector<int> sum(n+5);
for(int i = 1;i<=n;i++){
cin>>a[i];
sum[i] = sum[i-1] + a[i];
}
int tmp = 0;
for(int i = 1;i<=n;i++){
tmp += a[i];
pre[i] = pre[i-1];
if(tmp >= v){
tmp = 0;pre[i] ++;
}
}
tmp = 0;
for(int i = n;i>=1;i--){
tmp += a[i];
lst[i] = lst[i+1];
if(tmp >= v){
tmp = 0;lst[i] ++;
}
}
int ans = -1;
for(int r = 0;r<=n+1;r++){
int need = m - lst[r];
int p = lower_bound(pre.begin(),pre.begin()+1+n,need) - pre.begin();
if(r-1 >= p)
ans = max(ans,sum[r-1] - sum[p]);
}
cout<<ans<<endl;
return ;
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

D. Alice’s Adventures in Cards

题意

你在和$q,k,j$ 三个人玩纸牌游戏, 其他三个人,每个人的手中都有$1,2,…,n$这n张牌,但是每个人对于牌的重要程度认识是不同的。

你认为当$i > j$ 时,纸牌的重要程度$i > j$ ,即编号越高越重要。

其他三人认为的重要程度由排列$p^q,p^k,p^j$ 表示,数值越大的牌表示重要程度越大。

你只有一张纸牌1,你会根据三个人交换纸牌,你可以用a来交换b,当且仅当$a < b\ \& \ p_a > p_b $ 。

问你能否根据以上规则来交换到纸牌n,若能则输出一种方案。

思路

本题需要逆向处理,我们从n开始向前dp, 对每个玩家记录,能够和他交换的牌中,最小的p是多少。

当可以和某个玩家交换时,则更新三者的最小p值。

最终若能到达1那么就代表是有解的。同时我们记录dp的过程来找到具体的解决方案。

代码

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
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n;
cin>>n;
vector<vector<int> > p(3,vector<int>(n+1));
string s = "qkj";
rep(i,0,2) rep(j,1,n) cin>>p[i][j];
array<int,3> minp = {n,n,n};
vector<pair<char,int> > sol(n+5,{'?',-1});
for(int i = n-1;i>=1;i--){
int win = -1;
rep(j,0,2) if(p[j][i] > p[j][minp[j]]) win = j;
if(win == -1) continue;
sol[i] = {s[win],minp[win]};
rep(j,0,2) if(p[j][i] < p[j][minp[j]]) minp[j] = i;
}
if(sol[1].first == '?'){
cout<<"NO\n";return ;
}
cout<<"YES\n";
vector<pair<char,int> > ans;
ans.push_back(sol[1]);
while(ans.back().second != -1){
ans.push_back(sol[ans.back().second]);
}
ans.pop_back();
cout<<ans.size()<<'\n';
for(auto & [x,y]:ans){
cout<<x<<" "<<y<<'\n';
}
}
signed main(){
IO;
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}