比赛链接
A题
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
std::mt19937 rnd(time(0));
int n;
string s;
void solve(int test_case)
{
cin >> n >> s;
bool ok = true;
if (n % 2 == 0)
{
ok = false;
}
for (int i = 0; i < n / 2; i++)
{
if (s[i] != '1')
ok = false;
}
if (s[n / 2] != '/')
ok = false;
for (int i = n / 2 + 1; i < n; i++)
{
if (s[i] != '2')
ok = false;
}
if (ok)
{
cout << "Yes" << endl;
}
else
cout << "No" << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve(i);
}
return 0;
}
B题
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
std::mt19937 rnd(time(0));
int n;
string s;
void solve(int test_case)
{
cin >> s;
n = s.size();
bool ok = true;
if (n & 1)
ok = false;
map<char, int> mp;
for (char c : s)
mp[c]++;
for (int i = 1; i < n; i += 2)
{
if (s[i] != s[i - 1])
ok = false;
}
for (auto mpp : mp)
{
if (mpp.second != 2)
ok = false;
}
if (ok)
{
cout << "Yes" << endl;
}
else
cout << "No" << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve(i);
}
return 0;
}
C题
思路
对于 11 / 22 11/22 11/22字符串,我们考虑二分“/”左边 1 1 1的个数 x x x。
枚举原串中的每一个 / / /,如果其左边的长度为 x x x的子串全为 1 1 1,右边的长度为 x x x的子串全为 2 2 2,则为true,否则为false。
对于字符串的匹配问题,我们可以考虑使用字符串哈希,这样可以使判断区间字符串是否相等的时间复杂度将为 O ( 1 ) O(1) O(1)。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
std::mt19937 rnd(time(0));
int n;
char s[N];
// 双模哈希
const u64 base1 = 19260817, base2 = 1004535809;
int a[N], b[N];
int ha1[N], fac1[N], ha2[N], fac2[N], ha3[N], fac3[N];
void Hash()
{
fac1[0] = fac2[0] = fac3[0] = 1;
for (int i = 1; i <= n; i++)
{
fac1[i] = fac1[i - 1] * base1 % base2;
fac2[i] = fac2[i - 1] * base1 % base2;
fac3[i] = fac3[i - 1] * base1 % base2;
}
for (int i = 1; i <= n; i++)
{
ha1[i] = (ha1[i - 1] + a[i] * fac1[n - i] % base2) % base2;
ha2[i] = (ha2[i - 1] + b[i] * fac2[n - i] % base2) % base2;
ha3[i] = (ha3[i - 1] + (int)(s[i]) * fac3[n - i] % base2) % base2;
}
}
int gethash1(int l, int r)
{
return (ha1[r] - ha1[l - 1] + base2) * fac1[l] % base2;
}
int gethash2(int l, int r)
{
return (ha2[r] - ha2[l - 1] + base2) * fac2[l] % base2;
}
int gethash3(int l, int r)
{
return (ha3[r] - ha3[l - 1] + base2) * fac3[l] % base2;
}
bool check(int x)
{
bool ok = false;
for (int i = 1; i <= n; i++)
{
if (s[i] == '/')
{
int L = i - x;
int R = i + x;
if (L < 1)
continue;
if (R > n)
continue;
if (gethash1(L, i - 1) == gethash3(L, i - 1) && gethash2(i + 1, R) == gethash3(i + 1, R))
ok = true;
}
}
return ok;
}
void solve(int test_case)
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
a[i] = '1';
b[i] = '2';
}
Hash();
int low = 0, high = n;
while (low < high)
{
int mid = low + high + 1 >> 1;
if (check(mid))
{
low = mid;
}
else
high = mid - 1;
}
cout << low * 2 + 1 << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve(i);
}
return 0;
}
D题
思路
因为 X X X的长度为偶数,则其开头的第一个字符,在原串中的位置要么是奇数要么是偶数。
我们可以分别以奇数开头和以偶数开头,对两个相邻的字符进行合并。如果相邻的两个字符相等,则合并为一个,如果不相等,则合并成一个 − 1 -1 −1。
最后对于每一个被 − 1 -1 −1所隔开的区间,用双指针求出不包含重复数字的区间最大长度即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;
const int N = 3e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
std::mt19937 rnd(time(0));
int n;
int a[N], cnt[N];
int func(vector<int> &v)
{
int L = 0;
int sum = 0;
for (int R = 0; R < v.size(); R++)
{
cnt[v[R]]++;
while (cnt[v[R]] > 1)
cnt[v[L++]]--;
sum = max(sum, R - L + 1);
}
while (L < v.size())
cnt[v[L++]]--;
return sum * 2;
}
void solve(int test_case)
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
int ans = 0;
for (int j = 2; j <= 3; j++)
{
vector<int> v;
for (int i = j; i <= n; i += 2)
{
if (a[i] == a[i - 1])
{
v.push_back(a[i]);
}
else
{
ans = max(ans, func(v));
v.clear();
}
}
ans = max(ans, func(v));
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve(i);
}
return 0;
}
E题
思路
对于每一次查询的区间 [ L , R ] [L,R] [L,R],满足条件的子序列的长度,一定是一个凸函数。
因此我们可以使用前缀和对 1 1 1和 2 2 2进行预处理,使得枚举每一个"/"时,查询长度的时间复杂度都可以降至 O ( 1 ) O(1) O(1)。
对于求凸函数的最大值,我们使用二分查找即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;
const int N = 1e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
std::mt19937 rnd(time(0));
int n, q;
int a[N], b[N], nxt[N];
char s[N];
void solve(int test_case)
{
cin >> n >> q;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
for (int i = 1; i <= n; i++)
{
a[i] = a[i - 1];
b[i] = b[i - 1];
if (s[i] == '1') a[i]++;
if (s[i] == '2') b[i]++;
}
nxt[n + 1] = n + 1;
for (int i = n; i >= 1; i--)
{
nxt[i] = (s[i] == '/' ? i : nxt[i + 1]);
}
while (q--)
{
int l, r;
cin >> l >> r;
int k = nxt[l];
if (k > r)
{
cout << 0 << endl;
continue;
}
int low = l, high = r;
while (low < high)
{
int mid = low + high + 1 >> 1;
int k = nxt[mid];
if (k > r)
{
high = mid - 1;
continue;
}
if (a[k] - a[l - 1] <= b[r] - b[k])
{
low = mid;
}
else high = mid - 1;
}
int ans = min(a[nxt[low]] - a[l - 1], b[r] - b[nxt[low]]);
if (nxt[low + 1] <= r)
{
low = nxt[low + 1];
ans = max(ans, min(a[low] - a[l - 1], b[r] - b[low]));
}
cout << ans * 2 + 1 << endl;
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve(i);
}
return 0;
}
F题
思路
我们考虑使用二进制状压dp。
d p [ S ] dp[S] dp[S]表示取完集合 { S } \{S\} {S}内的所有元素所需要的最短前缀。
转移时枚举当前第 i i i个存在在集合中的元素,考虑从 d p [ S ⊕ 1 < < i ] dp[S \oplus 1 << i] dp[S⊕1<<i]进行转移。
我们可以使用二分查找,找到第一个不被 d p [ S ⊕ 1 < < i ] dp[S \oplus 1 << i] dp[S⊕1<<i]覆盖的 i i i的位置,然后转移即可。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
typedef long long i64;
typedef unsigned long long u64;
typedef pair<int, int> pii;
const int N = 2e5 + 5, M = 1e6 + 5;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f3f3f3f3f;
std::mt19937 rnd(time(0));
int n;
int a[N], dp[1ll << 20 + 5];
vector<int>pos[20];
void solve(int test_case)
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
a[i]--;
pos[a[i]].push_back(i);
}
fill(dp, dp + (1ll << 20) + 5, inf);
dp[0] = 0;
int ans = 0;
for (int val = 1; val < (1ll << 20); val++)
{
for (int i = 0; i < 20; i++)
{
if ((val >> i & 1) && dp[val ^ (1ll << i)] != inf)
{
int idx = upper_bound(pos[i].begin(), pos[i].end(), dp[val ^ (1ll << i)]) - pos[i].begin();
if (idx + 1 < pos[i].size())//因为每一次要加两个字符,所以是idx+1
{
dp[val] = min(dp[val], pos[i][idx + 1]);
}
}
}
if (dp[val] != inf)
{
ans = max(ans, (int)__builtin_popcount(val) * 2);
}
}
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int test = 1;
// cin >> test;
for (int i = 1; i <= test; i++)
{
solve(i);
}
return 0;
}