Atcoder ARC170

A - Yet Another AB Problem

对于几种情况,分类讨论一下:

  1. s[i]!=t[i],t[i]=='A':这种情况,需要向后找一个t[j]=='B'
  2. s[i]!=t[i],t[i]=='B':这种情况,需要向前找一个t[j]=='A'

若所有条件满足,则可以判定可行。要取得最小值,显然我们应当尽可能将1、2两种s[i]!=t[i]情况合起来(一次操作可以解决两个冲突),若是找不到,再与s[i]==t[i]的合起来(一次操作只解决一个冲突)。

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
69
70
71
72
73
74
75
76
77
78
// Problem: A - Yet Another AB Problem
// Contest: AtCoder - AtCoder Regular Contest 170
// URL: https://atcoder.jp/contests/arc170/tasks/arc170_a
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// Author: MaxDYF
//
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;
const int inf = 1 << 30;
const long long llinf = 1ll << 60;
const double PI = acos(-1);

#define lowbit(x) (x&-x)
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<db, db> pdd;
typedef pair<ll, int> pli;

int n, m, k, q;

void work()
{
cin >> n;
string s1, s2;
cin >> s1 >> s2;
if (s1 == s2)
{
cout << 0 << endl;
return;
}
bool isA = 0, needB = 0;
int cnt = 0, turnA = 0;
for (int i = 0; i < n; i++)
{
if (s1[i] == s2[i] && s1[i] == 'A')
isA = 1;
else if (s1[i] == s2[i])
needB = 0;
else if (s1[i] == 'A') // need to turn B
{
if (turnA > 0)
turnA--, needB = 0;
else if (isA)
cnt++, needB = 0;
else
{
cout << "-1\n";
return;
}
}
else // need to turn A
{
turnA++;
cnt++;
isA = 1;
needB = 1;
}
}
if (needB == 0)
cout << cnt << endl;
else
cout << -1 << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
work();
}

B - Arithmetic Progression Subsequence

容易想到通过枚举中间值,两边找等差数的想法。但是,很难解决子段重复计数的问题(一个区间可能包含多对等差数)。考虑其他做法。

注意到a[i]的值域很小(1~10),那么,最小的合法子段的长度也很小(至多21个数就会出现3个相等的数)。这样,我们可以暴力枚举子段,然后直接暴力判断字段的合法性。当第一次合法时直接计算剩余贡献即可。时间复杂度为\(\Theta(n\omega^4)\)\(\omega\)为值域),大约为\(O(1e9)\)左右,可以通过此题。

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
// Problem: B - Arithmetic Progression Subsequence
// Contest: AtCoder - AtCoder Regular Contest 170
// URL: https://atcoder.jp/contests/arc170/tasks/arc170_b
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// Author: MaxDYF
//
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;
const int inf = 1 << 30;
const long long llinf = 1ll << 60;
const double PI = acos(-1);

#define lowbit(x) (x&-x)
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<db, db> pdd;
typedef pair<ll, int> pli;

int n, m, k, q;

int a[N];
int pre[N][11];

void work()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
for (int j = 1; j <= 10; j++)
pre[i][j] = pre[i - 1][j];
pre[i][a[i]] = i;
}
auto check = [&](int l, int r)
{
for (int i = l;i <= r; i++)
for (int j = i + 1; j <= r; j++)
for (int k = j + 1; k <= r; k++)
if (a[j] - a[i] == a[k] - a[j])
return true;
return false;
};
ll ans = 0;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
if (check(i, j))
{
ans += n - j + 1;
break;
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
work();
}

C - Prefix Mex Sequence

一道很巧妙的dp题。

一开始,想的是令dp[i][j]表示前\(i\)项,\(mex=j\)的情况总数。然后发现状态非常复杂,无法判断之前放置了多少个大于\(mex\)的数,转移无法进行。

官方题解采用了一种很巧妙的思路避开了\(mex\)前后的不确定性。

\(mex\)的本质,就是一个不同于集合内任何一个数的数;将\(mex\)放进集合内,相当于令集合内不同数的个数+1。

于是,我们令dp[i][j]表示前\(i\)项,集合内有\(j\)个不同的数。接下来讨论:

  1. \(s[i]=1\):加入了一个\(mex\),相当于加入了一个不同于之前任意一个值的数,则直接转移: \[ f[i][j] =f[i-1][j-1] \]

  2. s[i]=0:加入一个不等于\(mex\)的数,则这个数可能是小于\(mex\)的数,也可能是大于的数。但是,归根结底,这只分为两种情况:

    1. 这个数在原来有的\(j\)个数内,总共\(j\)种可能性。
    2. 这个数不在原来的\(j\)个数内,总共\(m-j+1\)种可能性。

    那么,我们就可以根据这个写出递推方程: \[ f[i][j]=f[i-1][j]*j+f[i-1][j-1]*(m-j+1) \]

通过这样的方式,我们就很巧妙地避开了对\(mex\)的讨论,转而讨论更容易维护的种类数,从而在\(\Theta(n\min(n,m))\)的时间内完成了这道题目。

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
// Problem: C - Prefix Mex Sequence
// Contest: AtCoder - AtCoder Regular Contest 170
// URL: https://atcoder.jp/contests/arc170/tasks/arc170_c
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// Author: MaxDYF
//
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;

const int N = 5010;
const int inf = 1 << 30;
const long long llinf = 1ll << 60;
const double PI = acos(-1);

#define lowbit(x) (x&-x)
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<db, db> pdd;
typedef pair<ll, int> pli;

ll n, m, k, q, lim;
const ll mod = 998244353ll;
ll f[N][N];
void work()
{
cin >> n >> lim;
m = min(lim + 1, n);
f[0][0] = 1;
for (int i = 1, x; i <= n; i++)
{
cin >> x;
if (x == 1)
{
for (int j = 1; j <= m; j++)
f[i][j] = f[i - 1][j - 1];
}
else
{
for (int j = 1; j <= m; j++)
f[i][j] = (f[i - 1][j - 1] * (lim - j + 1) + f[i - 1][j] * (j)) % mod;
}

}
ll ans = 0;
for (int i = 0; i <= m; i++)
ans = (ans + f[n][i]) % mod;
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
work();
}