AtCoder Regular Contest 170 A-C
A - Yet Another AB Problem
贪心。
定义下标\(i\)满足\(S[i]=B,T[i]=A\)为\(BA\)型,\(S[i]=B,T[i]=A\)为\(AB\)型,\(AA\)型、\(BB\)型同理。
对所有\(BA\)型的下标\(i\)去匹配其右侧的第一个\(AB\)型的下标\(j\),匹配成功则对下标\(i\)和\(j\)进行操作,若无法匹配,则对剩余的\(BA\)型下标\(i\)去匹配其右侧的\(BB\)型下标\(j\)和对剩余的\(AB\)型下标\(i\)去匹配其左侧的\(AA\)型下标\(j\),匹配成功则对下标\(i\)和\(j\)进行操作,若无法匹配则输出\(-1\),能完成匹配则输出操作数。
时间复杂度:\(O(nlogn)\),用\(vector\)替代\(set\)可优化至\(O(n)\)
#include <bits/stdc++.h>
using namespace std;
#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int main()
{
cctie;
int n; cin >> n;
string S, T; cin >> S >> T;
set<int> atob, btoa;
for (int i = 0; i < n; i++)
{
if (S[i] == T[i]) continue;
if (S[i] == 'B' && T[i] == 'A') btoa.insert(i);
else atob.insert(i);
}
int ans = 0;
while (!btoa.empty() && !atob.empty() && *btoa.begin() < *(--atob.end()))
{
int k = *btoa.begin();
auto ita = atob.upper_bound(k);
int t = *ita;
S[k] = 'A', S[t] = 'B';
btoa.erase(k);
atob.erase(t);
ans++;
}
if (!atob.empty())
{
int k = n;
for (int i = 0; i < n; i++)
{
if (S[i] == T[i] && S[i] == 'A')
{
k = i;
break;
}
}
if (k < *atob.begin()) ans += atob.size();
else ans = -1;
}
if (ans != -1 && !btoa.empty())
{
int k = -1;
for (int i = n - 1; i >= 0; i--)
{
if (S[i] == T[i] && S[i] == 'B')
{
k = i;
break;
}
}
if (k > *(--btoa.end())) ans += btoa.size();
else ans = -1;
}
cout << ans << '\n';
return 0;
}
B - Arithmetic Progression Subsequence
枚举、二分。
记符合题目要求的区间为符合区间。
当\([l,r]\)为符合区间时,所有区间\([x,y],1\leq x\leq l,r\leq y \leq n\)必然也是符合区间,考虑对每一个下标\(i,1\leq i\leq n\)求出所有以\(i\)为左端点的符合区间的最小右端点\(r\),则以\(i\)为左端点的非符合区间数为\(r[i]-i\)。由于数组中每个数\(\in[1,10]\),可以枚举所有三元组\(i,j,p\),满足相邻两数差值相等即\(j-i=p-j=k\),然后用二分更新所有对应值为\(i\)的下标的最小右端点\(r\)。答案即为\(\frac{(n+1)n}{2}-\sum_{i=1}^n(r[i]-i)\)。
时间复杂度:\(O(nlogn)\)
#include <bits/stdc++.h>
using namespace std;
#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define i64 long long
#define all(x) x.begin(), x.end()
int main()
{
cctie;
int n; cin >> n;
vector<int> b(n + 1);
vector a(11, vector<int>(0));
for (int i = 1; i <= n; i++)
{
cin >> b[i];
a[b[i]].push_back(i);
}
vector<int> r(n + 2, n + 1);
for (int k = 1; k <= 4; k++)
{
for (int i = 1; i + 2 * k <= 10; i++)
{
int j = i + k;
int p = j + k;
for (auto ii : a[i])
{
auto jj = upper_bound(all(a[j]), ii);
if (jj == a[j].end()) break;
auto pp = upper_bound(all(a[p]), *jj);
if (pp == a[p].end()) break;
r[ii] = min(r[ii], *pp);
}
for (auto pp : a[p])
{
auto jj = upper_bound(all(a[j]), pp);
if (jj == a[j].end()) break;
auto ii = upper_bound(all(a[i]), *jj);
if (ii == a[i].end()) break;
r[pp] = min(r[pp], *ii);
}
}
}
for (int i = 1; i <= 10; i++)
for (int j = 2; j < a[i].size(); j++)
r[a[i][j - 2]] = min(r[a[i][j - 2]], a[i][j]);
i64 ans = 1LL * (1 + n) * n / 2;
for (int i = n; i >= 1; i--)
{
r[i] = min(r[i], r[i + 1]);
ans -= r[i] - i;
}
cout << ans << '\n';
return 0;
}
C - Prefix Mex Sequence
动态规划。
记符合题目要求的序列为符合序列。
\(dp[i][j]\)表示由前\(i\)个数组成的序列中值的种类数为\(j\)的符合序列数。
当\(S[i]=1\)时,第\(i\)个数只能选择前\(i-1\)个数的\(mex\),为新的数,种类数加一;
当\(S[i]=0\)时,第\(i\)个数可以选择除了前\(i-1\)个数的\(mex\)的所有\(\in[1, m]\)的数,若选择前\(i-1\)个数中出现过的数,则有\(j\)种选择,若选择剩余的数,则有\(m+1-j\)种选择。
状态转移方程如下:
若\(S[i]=1\),则\(dp[i][j] = dp[i - 1][j - 1]\);
若\(S[i]=0\),则\(dp[i][j] = dp[i - 1][j] * j + dp[i - 1][j - 1] * (m + 1 - j)\)。
可以合成一个方程:\(dp[i][j] = (1 - S[i]) * dp[i - 1][j] * j + dp[i - 1][j - 1] * (1 + (1 - S[i]) * (m - j))\)。
答案即为\(\sum_{j=1}^{\min(m+1,n)}dp[n][j]\)。
时间复杂度:\(O(n\min(n,m))\)
#include <bits/stdc++.h>
using namespace std;
#define cctie ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define i64 long long
const int N = 5010, mod = 998244353;
int n, m;
int S[N];
i64 dp[N][N];
int main()
{
cctie;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> S[i];
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= min(m + 1, i); j++)
dp[i][j] = ((1 - S[i]) * dp[i - 1][j] * j + dp[i - 1][j - 1] * (1 + (1 - S[i]) * (m - j))) % mod;
i64 ans = 0;
for (int j = 1; j <= min(m + 1, n); j++) ans += dp[n][j];
cout << ans % mod << '\n';
return 0;
}