多校省选模拟07
多校省选模拟7
路过中丹
题意
给你一个字符串 \(s\),每次问 \(s[l:r]\) 能不能配得上丹,一个字符串配得上丹,当且仅当,你可以通过若干次行走,把这个子串的每个位置都遍历了。一次行走,就是把从一个点 \(i\) 到一个不一样的点 \(j\),使得经过的路径是个回文串。
题解
我们发现,如果存在一个,长度为三的回文串,那么肯定就行。这是显然的。
然后发现,肯定就是长度为偶数的回文串,能覆盖这个区间。
然后,发现,对于覆盖一个点来说,我们只要考虑以他为端点的最短偶回文串就可以了,不妨设长度为别为 \(L_i\) 和 \(R_i\)。
那么对于 \(l\in (i-L_i+1,i]\) 并且 \(r\in [i,i+R_i-1)\) 的情况,这个 \(i\) 肯定不能被覆盖。
然后发现,这是个矩阵加,单点查询,扫描线即可。
#include <bits/stdc++.h>
using std::pair;
using std::set;
using std::vector;
const int MAXN = 1e6 + 10, INF = 0x3f3f3f3f, MOD = 998244353, G = 3, MOD2 = 1e9 + 7, G2 = 7;
template<typename T>
inline T min(const T &x, const T &y) {
return x < y ? x : y;
}
template<typename T>
inline T max(const T &x, const T &y) {
return x > y ? x : y;
}
auto Mod = [] (int x) -> int {
if (x < 0) {
return x + MOD;
}
else if (x >= MOD) {
return x - MOD;
}
else {
return x;
}
};
using std::cin;
using std::cout;
struct Line {
int l, r, h, val;
} e[MAXN * 2];
pair<pair<int, int>, int> q[MAXN];
set<int> val;
vector<int> v[MAXN];
int N, Q, L[MAXN], R[MAXN], sum[MAXN], sc[MAXN], ANS[MAXN], H1[MAXN], H2[MAXN], pw[MAXN], H12[MAXN], H22[MAXN], pw2[MAXN];
char s[MAXN];
inline void add(int a, int b) {
for (int i = a; i <= N; i += i & -i) {
sc[i] += b;
}
return;
}
inline int qry(int pos) {
int ret = 0;
for (int i = pos; i; i -= i & -i) {
ret += sc[i];
}
return ret;
}
int main() {
freopen("pass.in", "r", stdin);
freopen("pass.out", "w", stdout);
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
cin >> N >> s + 1 >> Q;
pw[0] = 1;
pw2[0] = 1;
for (int i = 1; i <= N; ++i) {
L[i] = R[i] = INF;
H12[i] = ((long long) H12[i - 1] * G2 + s[i]) % MOD2;
H1[i] = ((long long) H1[i - 1] * G + s[i]) % MOD;
pw[i] = (long long) pw[i - 1] * G % MOD;
pw2[i] = (long long) pw2[i - 1] * G2 % MOD2;
}
for (int i = N; i; --i) {
H22[i] = ((long long) H22[i + 1] * G2 + s[i]) % MOD2;
H2[i] = ((long long) H2[i + 1] * G + s[i]) % MOD;
}
auto h1 = [&] (int l, int r) -> int {
return Mod(H1[r] - (long long) H1[l - 1] * pw[r - l + 1] % MOD);
};
auto h2 = [&] (int l, int r) -> int {
return Mod(H2[l] - (long long) H2[r + 1] * pw[r - l + 1] % MOD);
};
auto h12 = [&] (int l, int r) -> int {
return (H12[r] - (long long) H12[l - 1] * pw2[r - l + 1] % MOD2 + MOD2) % MOD2;
};
auto h22 = [&] (int l, int r) -> int {
return (H22[l] - (long long) H22[r + 1] * pw2[r - l + 1] % MOD2 + MOD2) % MOD2;
};
for (int i = 1; i < N; ++i) {
int l = 1, r = min(i, N - i), ret = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (h1(i - mid + 1, i + mid) == h2(i - mid + 1, i + mid) && h12(i - mid + 1, i + mid) == h22(i - mid + 1, i + mid)) {
ret = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
if (~ret) {
v[i - ret + 1].push_back(i);
v[i + ret + 1].push_back(-i);
}
}
for (int i = 1; i <= N; ++i) {
for (auto &j: v[i]) {
if (j < 0) {
val.erase(-j);
}
else {
val.insert(j);
}
}
auto it = val.lower_bound(i);
if (it != val.end()) {
R[i] = 2 * (*it - i + 1);
}
if (it != val.begin()) {
--it;
L[i] = 2 * (i - *it);
}
}
for (int i = 2; i < N; ++i) {
sum[i] = sum[i - 1] + (s[i - 1] == s[i + 1]);
}
for (int i = 1; i <= N; ++i) {
int ql = max(1, i - L[i] + 2), qr = min(i + R[i] - 2, N);
e[2 * i - 1] = {i, qr, ql, 1};
e[2 * i] = {i, qr, i + 1, -1};
}
std::sort(e + 1, e + 1 + 2 * N, [&] (const Line &a, const Line &b) {
return a.h < b.h;
});
for (int i = 1; i <= Q; ++i) {
cin >> q[i].first.first >> q[i].first.second;
q[i].second = i;
}
std::sort(q + 1, q + 1 + Q);
for (int i = 1, j = 1; i <= Q; ++i) {
while (j <= 2 * N && e[j].h <= q[i].first.first) {
add(e[j].l, e[j].val);
add(e[j].r + 1, -e[j].val);
++j;
}
if (q[i].first.first != q[i].first.second) {
if (sum[q[i].first.second - 1] - sum[q[i].first.first]) {
ANS[q[i].second] = 1;
}
else {
ANS[q[i].second] = !qry(q[i].first.second);
}
}
}
// for (int i = 1; i <= N; ++i) {
// cout << L[i] << ' ' << R[i] << '\n';
// }
for (int i = 1; i <= Q; ++i) {
cout << ANS[i];
}
return 0;
}
膜拜大丹
题意
有 \(n\) 个 \(A\) 类点, \(m\) 个 \(B\) 类点,然后他们分别有自己的属性 \(a_i\) 或者 \(b_i\),满足 \(A\) 类点向着 \(1\sim a_i\) 的 \(B\) 类点连边,\(B\) 类点向着 \(1\sim b_i\) 的 \(A\) 类点连边,你在这个图上,然后要每次选出来一个环,然后每个点选的次数有个上限,问你最多能选出来多少环。
题解
我们首先可以发现,一个膜拜路径,其实是个二元环就够了,因为如果大于二元环的话,不妨设是 \(A1\rightarrow B1\rightarrow A2\rightarrow B2\rightarrow A1\)。
不妨设 \(A1<A2\),那么应该有 \(A1<A2<b[B1]\),那么肯定就是可以形成一个 \(A1,B1\) 的二元环了。
那么既然是二元环,那就类似于二分图匹配,我们考虑跑个网络流。这就有不少分了。
接下来,考虑,模拟最大流即可。
思考一下,发现,我们如果把这点都映射到一个二维平面直角坐标系里的话,\(A\) 里边的点,映射为 \((i,a_i)\),然后 \(B\) 里边的点映射为 \((b_i,i)\),然后考虑从 \(A\) 部的从右向左地开始做,每次呢,总是选出来合法的 \(y\) 最大的 \(B\) 类点。
使用这种方法,在考虑怎么去进行增广。发现,根本不可能有对于之前的操作的退流,然后直接跑就完了。
#include <bits/stdc++.h>
using std::pair;
using std::set;
using std::vector;
const int MAXN = 1e6 + 10, INF = 0x3f3f3f3f, MOD = 998244353, G = 3, MOD2 = 1e9 + 7, G2 = 7;
template<typename T>
inline T min(const T &x, const T &y) {
return x < y ? x : y;
}
template<typename T>
inline T max(const T &x, const T &y) {
return x > y ? x : y;
}
auto Mod = [] (int x) -> int {
if (x < 0) {
return x + MOD;
}
else if (x >= MOD) {
return x - MOD;
}
else {
return x;
}
};
using std::cin;
using std::cout;
struct Line {
int l, r, h, val;
} e[MAXN * 2];
pair<pair<int, int>, int> q[MAXN];
set<int> val;
vector<int> v[MAXN];
int N, Q, L[MAXN], R[MAXN], sum[MAXN], sc[MAXN], ANS[MAXN], H1[MAXN], H2[MAXN], pw[MAXN], H12[MAXN], H22[MAXN], pw2[MAXN];
char s[MAXN];
inline void add(int a, int b) {
for (int i = a; i <= N; i += i & -i) {
sc[i] += b;
}
return;
}
inline int qry(int pos) {
int ret = 0;
for (int i = pos; i; i -= i & -i) {
ret += sc[i];
}
return ret;
}
int main() {
freopen("pass.in", "r", stdin);
freopen("pass.out", "w", stdout);
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
cin >> N >> s + 1 >> Q;
pw[0] = 1;
pw2[0] = 1;
for (int i = 1; i <= N; ++i) {
L[i] = R[i] = INF;
H12[i] = ((long long) H12[i - 1] * G2 + s[i]) % MOD2;
H1[i] = ((long long) H1[i - 1] * G + s[i]) % MOD;
pw[i] = (long long) pw[i - 1] * G % MOD;
pw2[i] = (long long) pw2[i - 1] * G2 % MOD2;
}
for (int i = N; i; --i) {
H22[i] = ((long long) H22[i + 1] * G2 + s[i]) % MOD2;
H2[i] = ((long long) H2[i + 1] * G + s[i]) % MOD;
}
auto h1 = [&] (int l, int r) -> int {
return Mod(H1[r] - (long long) H1[l - 1] * pw[r - l + 1] % MOD);
};
auto h2 = [&] (int l, int r) -> int {
return Mod(H2[l] - (long long) H2[r + 1] * pw[r - l + 1] % MOD);
};
auto h12 = [&] (int l, int r) -> int {
return (H12[r] - (long long) H12[l - 1] * pw2[r - l + 1] % MOD2 + MOD2) % MOD2;
};
auto h22 = [&] (int l, int r) -> int {
return (H22[l] - (long long) H22[r + 1] * pw2[r - l + 1] % MOD2 + MOD2) % MOD2;
};
for (int i = 1; i < N; ++i) {
int l = 1, r = min(i, N - i), ret = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (h1(i - mid + 1, i + mid) == h2(i - mid + 1, i + mid) && h12(i - mid + 1, i + mid) == h22(i - mid + 1, i + mid)) {
ret = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
if (~ret) {
v[i - ret + 1].push_back(i);
v[i + ret + 1].push_back(-i);
}
}
for (int i = 1; i <= N; ++i) {
for (auto &j: v[i]) {
if (j < 0) {
val.erase(-j);
}
else {
val.insert(j);
}
}
auto it = val.lower_bound(i);
if (it != val.end()) {
R[i] = 2 * (*it - i + 1);
}
if (it != val.begin()) {
--it;
L[i] = 2 * (i - *it);
}
}
for (int i = 2; i < N; ++i) {
sum[i] = sum[i - 1] + (s[i - 1] == s[i + 1]);
}
for (int i = 1; i <= N; ++i) {
int ql = max(1, i - L[i] + 2), qr = min(i + R[i] - 2, N);
e[2 * i - 1] = {i, qr, ql, 1};
e[2 * i] = {i, qr, i + 1, -1};
}
std::sort(e + 1, e + 1 + 2 * N, [&] (const Line &a, const Line &b) {
return a.h < b.h;
});
for (int i = 1; i <= Q; ++i) {
cin >> q[i].first.first >> q[i].first.second;
q[i].second = i;
}
std::sort(q + 1, q + 1 + Q);
for (int i = 1, j = 1; i <= Q; ++i) {
while (j <= 2 * N && e[j].h <= q[i].first.first) {
add(e[j].l, e[j].val);
add(e[j].r + 1, -e[j].val);
++j;
}
if (q[i].first.first != q[i].first.second) {
if (sum[q[i].first.second - 1] - sum[q[i].first.first]) {
ANS[q[i].second] = 1;
}
else {
ANS[q[i].second] = !qry(q[i].first.second);
}
}
}
// for (int i = 1; i <= N; ++i) {
// cout << L[i] << ' ' << R[i] << '\n';
// }
for (int i = 1; i <= Q; ++i) {
cout << ANS[i];
}
return 0;
}