20241104
T1
火星人的 DNA
显然单调,直接双指针。过程中维护每个限制还需要多少对应字符才能达成,拿个 set 每次取最大值和 \(0\) 比较即可判断当前是否合法。
代码
#include <iostream>
#include <set>
using namespace std;
multiset<int> st;
int n, k, q, ans = 2147483647;
int a[200005];
int req[200005];
void Add(int x, int y) {
x = a[x];
st.erase(st.find(req[x]));
req[x] += y;
st.insert(req[x]);
}
int main() {
freopen("dna.in", "r", stdin);
freopen("dna.out", "w", stdout);
cin >> n >> k >> q;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1, x, y; i <= q; i++) {
cin >> x >> y;
req[x] = y;
}
for (int i = 0; i < k; i++) st.insert(req[i]);
for (int i = 1, j = 0; i <= n; i++) {
while (j < n && *st.rbegin() > 0) Add(++j, -1);
if (*st.rbegin() <= 0) {
ans = min(ans, j - i + 1);
// cout << i << " " << j << "\n";
}
Add(i, 1);
}
if (ans == 2147483647)
cout << "impossible\n";
else
cout << ans << "\n";
return 0;
}
T2
石头
发现最大值是特殊的,在最大值左边的东西会先把左边扩展完,然后挨个扩展右边。右边的同理。于是可以考虑启发式分治,每次根据最大值递归,并考虑两边互相以及当前分治中心的贡献。两边互相的贡献是容易统计的,右边的所有起始位置会向位置在左边的、满足特定条件的所有询问产生贡献。特定条件指的是 \(p + k = C\) 或 \(p - k = C\)。左边向右边同理。然后考虑当前分治中心对区间内所有询问的贡献。我们考虑枚举左右之中短的一边,则每次形如先走一个数,然后扩展另外一边的一段,然后再走一个数,以此类推。每次走一个位置时对当前位置上的询问产生的贡献是容易计算的,随便拿个什么东西记录即可。而对于另外一边,其实也是对于位置在一段区间内的、满足 \(p + k = C\) 或 \(p - k = C\) 的所有询问产生贡献。所有贡献离线下来按横坐标升序枚举,过程中数组维护每个位置的值即可。注意加和减的限制要分开统计。
代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <cassert>
#include <map>
// #define int long long
using namespace std;
int n, q;
int a[100005];
pair<int, int> mx[17][100005];
int lg2[100005];
int Qmax(int l, int r, int t) {
assert(r >= l);
int k = lg2[r - l + 1];
pair<int, int> tmp = max(mx[k][l], mx[k][r - (1 << k) + 1]);
return t ? tmp.second : tmp.first;
}
int p[100005], k[100005];
struct qquery {
int x, y, v, id;
} qs1[500005], qs2[500005];
int ASDF[400005];
int ans[100005];
int *val = &ASDF[200002];
int qcnt1, qcnt2;
map<int, int> mp[100005];
void Solve(int l, int r) {
if (l > r)
return;
// cerr << l << " " << r << "\n";
int x = Qmax(l, r, 1);
if (x != r) {
int a = r - x;
qs1[++qcnt1] = (qquery) { l, a + x + 1, a, 0 };
qs1[++qcnt1] = (qquery) { x + 1, a + x + 1, -a, 0 };
}
if (x != l) {
int a = x - l;
qs2[++qcnt2] = (qquery) { x, a - x + 1, a, 0 };
qs2[++qcnt2] = (qquery) { r + 1, a - x + 1, -a, 0 };
}
if (r - x <= x - l) {
for (int i = x, cur = 1, cl = x; i <= r; i++) {
++mp[i][cur];
int ll = l, rr = cl - 1, mid, ans = cl;
while (ll <= rr) {
mid = (ll + rr) >> 1;
if (Qmax(mid, cl - 1, 0) < a[i + 1])
ans = mid, rr = mid - 1;
else
ll = mid + 1;
}
if (ans != cl) {
qs1[++qcnt1] = (qquery) { ans, cur + cl, 1, 0 };
qs1[++qcnt1] = (qquery) { cl, cur + cl, -1, 0 };
}
cur += (cl - ans), cl = ans;
++cur;
}
} else {
for (int i = x, cur = 1, cr = x; i >= l; i--) {
++mp[i][cur];
int ll = cr + 1, rr = r, mid, ans = cr;
while (ll <= rr) {
mid = (ll + rr) >> 1;
if (Qmax(cr + 1, mid, 0) < a[i - 1])
ans = mid, ll = mid + 1;
else
rr = mid - 1;
}
if (ans != cr) {
qs2[++qcnt2] = (qquery) { cr + 1, cur - cr, 1, 0 };
qs2[++qcnt2] = (qquery) { ans + 1, cur - cr, -1, 0 };
}
cur += (ans - cr), cr = ans;
++cur;
}
}
Solve(l, x - 1);
Solve(x + 1, r);
}
signed main() {
freopen("stone.in", "r", stdin);
freopen("stone.out", "w", stdout);
cin >> n >> q;
a[0] = a[n + 1] = 2147483647;
lg2[0] = -1;
for (int i = 1; i <= n; i++) cin >> a[i], mx[0][i] = make_pair(a[i], i), lg2[i] = lg2[i - 1] + ((i & (i - 1)) == 0);
for (int i = 1; (1 << i) <= n; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++)
mx[i][j] = max(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
}
// cerr << qcnt1 << " " << qcnt2 << "\n";
Solve(1, n);
for (int i = 1; i <= q; i++) {
cin >> p[i] >> k[i];
qs1[++qcnt1] = (qquery) { p[i], k[i] + p[i], 0, i };
qs2[++qcnt2] = (qquery) { p[i], k[i] - p[i], 0, i };
}
sort(qs1 + 1, qs1 + qcnt1 + 1, [](qquery a, qquery b) { return a.x == b.x ? (a.id < b.id) : (a.x < b.x); });
for (int i = 1; i <= qcnt1; i++) {
if (qs1[i].id)
ans[qs1[i].id] += val[qs1[i].y];
else
val[qs1[i].y] += qs1[i].v;
}
memset(ASDF, 0, sizeof ASDF);
sort(qs2 + 1, qs2 + qcnt2 + 1, [](qquery a, qquery b) { return a.x == b.x ? (a.id < b.id) : (a.x < b.x); });
for (int i = 1; i <= qcnt2; i++) {
if (qs2[i].id)
ans[qs2[i].id] += val[qs2[i].y];
else
val[qs2[i].y] += qs2[i].v;
}
for (int i = 1; i <= q; i++) cout << ans[i] + mp[p[i]][k[i]] << "\n";
return 0;
}
T3
字符串问题
将每个长度为 \(2\) 的子串视为边,则构成三个点的有向图,要求图中欧拉回路 / 路径的数量。直接套 BEST 定理即可。对于欧拉路径,只需要枚举起点终点,然后就一样做。
代码
#include <iostream>
#include <string.h>
#define int long long
using namespace std;
const int P = 1000000007;
int qpow(int x, int y) {
int ret = 1;
while (y) {
if (y & 1)
ret = ret * x % P;
y >>= 1;
x = x * x % P;
}
return ret;
}
struct DSU {
int f[10];
void ini(int x) { for (; ~x; --x) f[x] = x; }
int getf(int x) { return (f[x] == x ? x : (f[x] = getf(f[x]))); }
void Merge(int x, int y) {
x = getf(x), y = getf(y);
(x != y) ? (f[x] = y) : 0;
}
} dsu;
int A[10][10];
int g[10][10];
int out[10], in[10];
int n;
int det() {
for (int i = 0; i < n; i++) A[i][i] = out[i];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++)
A[i][j] = (A[i][j] + P - g[i][j]) % P;
}
if (n == 3)
return (A[0][0] * A[1][1] - A[0][1] * A[1][0] % P + P) % P;
if (n == 2)
return A[0][0];
return 1;
}
int a[20];
bool Isolated[10];
int fac[1000005];
int ifac[1000005];
int coef = 1;
signed main() {
freopen("str.in", "r", stdin);
freopen("str.out", "w", stdout);
ifac[0] = fac[0] = 1;
for (int i = 1; i <= 1000000; i++) fac[i] = fac[i - 1] * i % P, ifac[i] = qpow(fac[i], P - 2);
int t;
cin >> t;
cin >> t;
while (t--) {
memset(Isolated, 0, sizeof Isolated);
memset(out, 0, sizeof out);
memset(in, 0, sizeof in);
memset(g, 0, sizeof g);
memset(A, 0, sizeof A);
memset(a, 0, sizeof a);
n = 0;
coef = 1;
string str;
cin >> str;
int asdf = str.size();
if (asdf == 1) {
cout << "3\n";
continue;
}
for (int i = 0; i < asdf - 1; i++) ++a[(str[i] - 'a') * 3 + (str[i + 1] - 'a')];
dsu.ini(3);
for (int i = 0; i < 9; i++) {
int x = i / 3, y = i % 3;
g[x][y] += a[i];
out[x] += a[i], in[y] += a[i];
coef = coef * ifac[a[i]] % P;
if (a[i])
dsu.Merge(x, y);
}
int lst = -1;
for (int i = 0; i < 3; i++) {
int x = dsu.getf(i);
if (!out[x] && !in[x]) {
Isolated[i] = 1;
continue;
}
lst = x;
++n;
}
for (int i = 0, t = 0; i < 3; i++) {
if (Isolated[i])
continue;
for (int j = 0; j < 3; j++) g[t][j] = g[i][j];
in[t] = in[i];
out[t] = out[i];
++t;
}
for (int i = 0, t = 0; i < 3; i++) {
if (Isolated[i])
continue;
for (int j = 0; j < 3; j++) g[j][t] = g[j][i];
++t;
}
int s = -1, t = -1, cnt = 0;
for (int i = 0; i < n; i++) {
(out[i] == in[i] + 1) ? (s = i) : 0;
(in[i] == out[i] + 1) ? (t = i) : 0;
(in[i] == out[i]) ? (++cnt) : 0;
}
if (s != -1 && t != -1) {
++in[s], ++out[t];
g[t][s]++;
cnt += 2;
}
if (s != -1) {
int tmp = det() * coef % P;
for (int i = 0; i < n; i++) tmp = tmp * fac[in[i] - 1] % P;
cout << tmp << "\n";
} else {
int s = 0;
for (int i = 0; i < n; i++) s += out[i];
int tmp = det() * coef % P * s % P;
for (int i = 0; i < n; i++) tmp = tmp * fac[in[i] - 1] % P;
cout << tmp << "\n";
}
}
return 0;
}
T4
基础图论练习题
不会。

浙公网安备 33010602011771号