Educational Codeforces Round 103 EFG 题解
Educational Codeforces Round 103
E - Pattern Matching
在每一个限制条件中,\(s_j\) 可以和 \(16\) 个串匹配(每个位置是不是 _ ,\(2^4=16\))。把这些串都找出来,要求 \(mt_j\) 排在最前面,从其它串向 \(mt_j\) 连一条边,表示先后先后关系,然后一遍拓扑
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); int f = 0; x = 0;
while (!isdigit(ch)) { if (ch == '-') f = 1; ch = getchar(); }
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
if (f) x = -x;
} const int N = 1e5 + 5;
int n, m, k, p[N]; char a[N][5], b[5], c[5];
int f (char *p) {
return p[0] * 1000000 + p[1] * 10000 + p[2] * 100 + p[3];
}
struct qwq { int id, num; } u[N]; int uu[N];
bool cmp (qwq a, qwq b) { return a.num < b.num; }
vector<int> g[N]; int o[N];
void add (int u, int v) { g[u].push_back (v), ++o[v]; }
queue<int> q;
void work (int w) {
for (int i = 0; i < (1 << k); ++i) {
int tag = 0;
for (int j = 0; j < k; ++j)
if ((i >> j) & 1) c[j] = b[j]; else c[j] = '_';
int cc = f (c);
int t = lower_bound (uu + 1, uu + n + 1, cc) - uu;
if (uu[t] == cc && u[t].id != w) add (u[t].id, w);
}
}
signed main() {
read (n), read (m), read (k);
for (int i = 1; i <= n; ++i) scanf ("%s", a[i]);
for (int i = 1; i <= n; ++i) u[i].id = i, u[i].num = f (a[i]);
sort (u + 1, u + n + 1, cmp);
for (int i = 1; i <= n; ++i) uu[i] = u[i].num;
for (int i = 1, w; i <= m; ++i) {
scanf ("%s %d", b, &w), work (w);
for (int j = 0; j < k; ++j)
if (a[w][j] != '_' && a[w][j] != b[j]) { return puts ("NO"), 0; }
}
for (int i = 1; i <= n; ++i) if (!o[i]) q.push (i);
int now = n;
while (!q.empty()) {
int u = q.front(); q.pop(); p[now--] = u;
for (int v : g[u]) if (!(--o[v])) q.push (v);
}
if (now) { return puts ("NO"), 0; } puts ("YES");
for (int i = 1; i <= n; ++i) printf ("%d ", p[i]); puts ("");
}
F - Lanterns
用 \(f_i\) 表示前 \(i\) 盏灯笼能覆盖的最长前缀。如何转移?
枚举 \(j\) ,如果 \(f_j \ge i\),则 \((j,i]\) 区间的全都可以朝右,答案为 \(max_{i=j+1}^{i}{i+a_i}\)
如果 \(f_j\ge i-a_i\),则 \(i\) 朝左,\((j,i)\) 内的全部超右,答案类似
由于 \(f_j\) 是单调的,所以符合条件的 \(j\) 的边界可以二分出来,然后记录一下是从哪里转移的就可以输出方案了
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); int f = 0; x = 0;
while (!isdigit(ch)) { if (ch == '-') f = 1; ch = getchar(); }
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
if (f) x = -x;
} const int N = 3e5 + 5;
int n, a[N], f[N], fr[N], d[N], k[N], lg[N];
#define cmax(x, y) (x = x > y ? x : y)
struct ans { int v, w; } ;
struct st {
int c[N][19];
void pre () {
for (int i = 0; i <= n; ++i) c[i][0] = a[i] + i;
for (int i = 1; (1 << i) <= n; ++i)
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
c[j][i] = max (c[j][i - 1], c[j + (1 << i - 1)][i - 1]);
}
int query (int l, int r) {
if (l > r) return 0; int t = lg[r - l + 1];
// printf ("%d %d %d\n", l, r, max (c[l][t], c[r - (1 << t) + 1][t]));
return max (c[l][t], c[r - (1 << t) + 1][t]);
}
} A;
signed main() {
int T; read (T); lg[0] = -1;
for (int i = 1; i <= 300002; ++i) lg[i] = lg[i >> 1] + 1;
while (T--) {
read (n); a[++n] = 0;
for (int i = 1; i < n; ++i) read (a[i]); A.pre ();
for (int i = 0; i <= n; ++i) f[i] = k[i] = fr[i] = 0;
for (int i = 1, l, r, m, j, mx = -1, w = 0; i <= n; ++i) {
l = 0, r = i - 1, j = -1;
while (l <= r) {
m = l + r >> 1;
if (f[m] + 1 >= i - a[i]) j = m, r = m - 1;
else l = m + 1;
}
if (j >= 0) f[i] = max (A.query (j + 1, i - 1), i - 1), fr[i] = j;
if (f[i - 1] >= i && f[i] < i + a[i]) f[i] = i + a[i], fr[i] = i - 1, k[i] = 1;
if (mx > f[i]) f[i] = mx, fr[i] = w; else mx = f[i], w = i;
// printf ("666 %d %d %d %d\n", j, i, f[i], fr[i]);
}
if (f[n] >= n - 1) {
puts ("YES");
for (int i = n; i >= 1; i = fr[i]) {
for (int j = fr[i] + 1; j < i; ++j) d[j] = 1; d[i] = k[i];
}
for (int i = 1; i < n; ++i) putchar (d[i] ? 'R' : 'L'); puts ("");
} else puts ("NO");
}
}
G - Minimum Difference
使用莫队解决,那么操作 \(2\) 就是把普通莫队换成回滚莫队,可以不用管
根据题面肯定得维护 \(cnt\) 数组记录每个数出现的次数
然后还得维护 \(num\) 维护每种 \(cnt\) 出现的次数
例如,当前的数列为 \(1,1,1,2,2,2,3\),则 \(cnt={3,3,1}\),\(num={1,0,2}\) (从 \(1\) 开始)
如果单单维护 \(num,cnt\) 很简单,但仅仅维护两个数组并不能快速回答询问
先讲讲询问的回答方法:\(cnt\) 的种类的数量是 \(O(\sqrt{n})\) 的(\(1+2+3+...+2\sqrt{n}\ge n\)),所以需要把这些有用的状态提取出来,然后就可以通过指针扫描 \(O(\sqrt{n})\) 回答单词询问
维护有用的 \(num\) 可以转换成维护一些区间, \(num\) 中的值就是这些区间的长度,当 \(cnt\) 改变的时候只需要 \(O(1)\) 维护区间边界。对于每次询问“跳”区间直到超出边界,所以只有非空的区间才会遍历到
总复杂度 \(O(n^{\frac{5}{3}}+m\sqrt{n})\)
#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 3e5 + 5;
int n, m, sz, nn, a[N], bl[N], res[N];
struct qwq {
int l, r, k, c, id;
bool operator < (const qwq &p) const {
return bl[l] ^ bl[p.l] ? bl[l] < bl[p.l] : bl[r] ^ bl[p.r] ? bl[r] < bl[p.r] : id < p.id;
}
} b[N];
struct qaq { int w, x, y; } c[N]; int nb, nc;
int l = 1, r = 0, cnt[N], ll[N], rr[N], co[N], s[N], cc[N];
void change (int x) {
co[rr[cnt[x]]] = cnt[x] + 1, --rr[cnt[x]], --ll[cnt[x] + 1], ++cnt[x];
}
void _change (int x) {
co[ll[cnt[x]]] = cnt[x] - 1, ++ll[cnt[x]], ++rr[cnt[x] - 1], --cnt[x];
}
void modify (int p) {
if (l <= c[p].w && c[p].w <= r) change (c[p].y), _change (c[p].x); a[c[p].w] = c[p].y;
}
void _modify (int p) {
if (l <= c[p].w && c[p].w <= r) change (c[p].x), _change (c[p].y); a[c[p].w] = c[p].x;
}
int query (int k) {
int u = 0, res = n + 1;
for (int i = 1; i <= n; i = rr[co[i]] + 1)
if (co[i]) s[++u] = rr[co[i]] - ll[co[i]] + 1, cc[u] = co[i];
for (int i = 1, j = 0, ss = 0; i <= u; ++i) {
while (ss < k && j < u) ++j, ss += s[j];
if (ss >= k) res = min (res, cc[j] - cc[i]); ss -= s[i];
}
return res > n ? -1 : res;
}
signed main() {
read (n), read (m);
sz = pow (n, 0.666); nn = n / sz; int L = 0, R = 0;
for (int i = 1; i <= nn; ++i) {
L = R + 1, R += sz;
for (int j = L; j <= R; ++j) bl[j] = i;
}
for (int i = R + 1; i <= n; ++i) bl[i] = nn + 1;
for (int i = 1; i <= n; ++i) read (a[i]);
for (int i = 1, o, l, r, k; i <= m; ++i) {
read (o);
if (o == 1) read (l), read (r), read (k), b[++nb] = { l, r, k, nc, nb };
else read (r), read (k), c[++nc] = { r, a[r], k }, a[r] = k;
}
sort (b + 1, b + nb + 1);
int now = nc; ll[0] = 1, rr[0] = n;
for (int i = 1; i <= n; ++i) ll[i] = n + 1, rr[i] = n;
for (int i = 1; i <= nb; ++i) {
// printf ("2333 %d %d %d %d\n", b[i].id, b[i].l, b[i].r, b[i].k);
while (now < b[i].c) ++now, modify (now);
while (now > b[i].c) _modify (now), --now;
while (l < b[i].l) _change (a[l]), ++l;
while (l > b[i].l) --l, change (a[l]);
while (r < b[i].r) ++r, change (a[r]);
while (r > b[i].r) _change (a[r]), --r;
res[b[i].id] = query (b[i].k);
}
for (int i = 1; i <= nb; ++i) printf ("%d\n", res[i]);
return 0;
}