版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82420556
【比赛链接】
【题解链接】
**【A】**Packets
【思路要点】
- 可以用 构造一组最优的可行方案。
- 其中 为使得 的最大的 。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int main() {
int n; read(n);
int ans = 1;
for (int i = 1; i < n; i <<= 1) {
n -= i;
ans++;
}
writeln(ans);
return 0;
}
**【B】**Reach Median
【思路要点】
- 令原数列中位数为 ,中间位置为 。
- 若 ,应当将所有 中所有不足 的数补足 。
- 若 ,应当将所有 中所有大于 的数减至 。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int a[MAXN], s, n;
int main() {
read(n), read(s);
for (int i = 1; i <= n; i++)
read(a[i]);
sort(a + 1, a + n + 1);
int m = (n + 1) / 2;
long long ans = 0;
if (a[m] >= s) {
for (int i = m; i >= 1; i--)
if (a[i] >= s) ans += a[i] - s;
} else {
for (int i = m; i <= n; i++)
if (a[i] <= s) ans += s - a[i];
}
writeln(ans);
return 0;
}
**【C】**Equalize
【思路要点】
- 我们发现使用 号操作会使方案变优当且仅当这次操作交换了相邻的两个位置,并且同时使得这两个位置从不合法变得合法。
- 计算出能够进行的这样的 号操作的个数,剩余的操作用 号操作完成。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n; bool a[MAXN];
char s[MAXN], t[MAXN];
int main() {
read(n);
scanf("\n%s", s + 1);
scanf("\n%s", t + 1);
int ans = 0, mns = 0;
for (int i = 1; i <= n; i++) {
a[i] = s[i] != t[i];
ans += a[i];
if (a[i] && a[i - 1] && s[i] != s[i - 1]) {
a[i] = false;
mns++;
}
}
writeln(ans - mns);
return 0;
}
**【D】**Valid BFS?
【思路要点】
- 将每一个点的出边按照出现在给定的 序中的位置排序,从 号点开始进行一次 ,判断给定的 序是否与得到的 序完全相同即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
vector <int> a[MAXN];
int n, b[MAXN], home[MAXN], depth[MAXN];
void dfs(int pos, int fa) {
depth[pos] = depth[fa] + 1;
for (unsigned i = 0; i < a[pos].size(); i++)
if (a[pos][i] != fa) dfs(a[pos][i], pos);
}
bool cmp(int x, int y) {
return home[x] < home[y];
}
int main() {
read(n);
for (int i = 1; i <= n - 1; i++) {
int x, y; read(x), read(y);
a[x].push_back(y);
a[y].push_back(x);
}
for (int i = 1; i <= n; i++)
read(b[i]), home[b[i]] = i;
dfs(1, 0);
for (int i = 1; i <= n; i++)
if (depth[b[i]] < depth[b[i - 1]]) {
printf("No\n");
return 0;
}
for (int i = 1; i <= n; i++)
sort(a[i].begin(), a[i].end(), cmp);
static int q[MAXN];
static bool vis[MAXN];
int l = 1, r = 1;
q[l] = 1; vis[1] = true;
while (l <= r) {
int tmp = q[l++];
for (unsigned i = 0; i < a[tmp].size(); i++)
if (!vis[a[tmp][i]]) {
vis[a[tmp][i]] = true;
q[++r] = a[tmp][i];
}
}
for (int i = 1; i <= n; i++)
if (q[i] != b[i]) {
printf("No\n");
return 0;
}
printf("Yes\n");
return 0;
}
**【E】**Trips
【思路要点】
- 将问题倒过来考虑,我们考虑先求出最后一个时刻的答案。
- 假设所有点都被选取,那么如果某个点的度数不足 ,那么显然它不能被选取,我们就删除它以及它的所有邻边,如此重复直到所有留在图中的点度数都大于等于 。
- 那么前面一个时刻的答案即为删去一条指定的边后图中剩余的点数。
- 显然每一个点/边都只会被删除一次。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m, k, d[MAXN], x[MAXN], y[MAXN], res[MAXN];
bool vis[MAXN], del[MAXN];
vector <int> a[MAXN], b[MAXN];
int main() {
read(n), read(m), read(k);
for (int i = 1; i <= m; i++) {
read(x[i]), read(y[i]);
a[x[i]].push_back(y[i]);
b[x[i]].push_back(i);
a[y[i]].push_back(x[i]);
b[y[i]].push_back(i);
d[x[i]]++, d[y[i]]++;
}
static int q[MAXN];
int l = 0, r = -1, ans = n;
for (int i = 1; i <= n; i++)
if (d[i] < k) {
vis[i] = true;
q[++r] = i;
ans--;
}
for (int j = m; j >= 1; j--) {
while (l <= r) {
int tmp = q[l++];
for (unsigned i = 0; i < a[tmp].size(); i++)
if (!del[b[tmp][i]]) {
del[b[tmp][i]] = true;
d[tmp]--, d[a[tmp][i]]--;
if (!vis[a[tmp][i]] && d[a[tmp][i]] < k) {
vis[a[tmp][i]] = true;
q[++r] = a[tmp][i];
ans--;
}
}
}
res[j] = ans;
if (!del[j]) {
del[j] = true;
d[x[j]]--, d[y[j]]--;
if (!vis[x[j]] && d[x[j]] < k) {
vis[x[j]] = true;
q[++r] = x[j];
ans--;
}
if (!vis[y[j]] && d[y[j]] < k) {
vis[y[j]] = true;
q[++r] = y[j];
ans--;
}
}
}
for (int i = 1; i <= m; i++)
writeln(res[i]);
return 0;
}
**【F】**Maximum Reduction
【思路要点】
- 不难发现题目给出的代码其实就是求出了所有长度为 的区间最大值的和。
- 从小到大考虑每一个数被计算了多少次即可。
- 具体实现时可以用并查集维护每一个数作为区间最大值的极大区间的左右端点。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
const int P = 1e9 + 7;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
bool vis[MAXN];
int n, k, a[MAXN], f[MAXN], home[MAXN], l[MAXN], r[MAXN];
bool cmp(int x, int y) {
return a[x] < a[y];
}
int F(int x) {
if (f[x] == x) return x;
else return f[x] = F(f[x]);
}
void merge(int x, int y) {
x = F(x), y = F(y);
if (x == y) return;
f[x] = y;
chkmax(r[y], r[x]);
chkmin(l[y], l[x]);
}
int calc(int len) {
int lim = len / (k - 1);
int cnt = 1ll * len * lim % P;
cnt -= lim * (lim + 1ll) / 2 % P * (k - 1) % P;
cnt = (cnt + P) % P;
return cnt;
}
int main() {
read(n), read(k);
for (int i = 1; i <= n; i++) {
read(a[i]), home[i] = i;
f[i] = i; l[i] = r[i] = i;
}
sort(home + 1, home + n + 1, cmp);
int ans = 0;
for (int i = 1; i <= n; i++) {
int tmp = home[i];
vis[tmp] = true;
if (vis[tmp - 1]) merge(tmp, tmp - 1);
if (vis[tmp + 1]) merge(tmp, tmp + 1);
int tnp = F(tmp), cnt = calc(r[tnp] - l[tnp] + 1);
cnt = (cnt - calc(tmp - l[tnp]) + P) % P;
cnt = (cnt - calc(r[tnp] - tmp) + P) % P;
ans = (ans + 1ll * cnt * a[tmp]) % P;
}
writeln(ans);
return 0;
}
**【G】**A Game on Strings
【思路要点】
- 对于每一个字母 ,将所有 的位置断开,计算得到的每一段的每一个前缀/后缀的 函数,借助前缀异或和,在询问时只需要枚举 的第一步就可以计算出局面的 函数。细节比较多。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int MAXP = 5200005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct info {int l, r, len; } b[MAXP];
char s[MAXN]; int n, q, tot, a[MAXN];
int sg[MAXN][27][2], sum[MAXN][27], last;
int pre[MAXN][27], suf[MAXN][27];
bool cmp(info a, info b) {
if (a.r == b.r) return a.len < b.len;
else return a.r < b.r;
}
int main() {
scanf("%s", s + 1);
n = strlen(s + 1), read(q);
for (int i = 1; i <= n; i++)
a[i] = s[i] - 'a';
a[0] = a[n + 1] = 26;
b[tot = 1] = (info) {1, n, n};
for (int c = 0; c <= 25; c++) {
int pos = 1;
for (int i = 1; i <= n; i++)
if (a[i] == c) {
if (i != pos) {
b[++tot] = (info) {pos, i - 1, i - pos};
static int tmp[26];
memset(tmp, 0, sizeof(tmp));
for (int j = pos; j <= i - 1; j++)
tmp[a[j]] = j;
for (int j = 0; j <= 25; j++)
if (tmp[j] != 0 && tmp[j] != i - 1) b[++tot] = (info) {tmp[j] + 1, i - 1, i - (tmp[j] + 1)};
}
pos = i + 1;
}
if (pos <= n) b[++tot] = (info) {pos, n, n - pos + 1};
}
sort(b + 1, b + tot + 1, cmp);
for (int i = 1; i <= tot; i++) {
static int Min[26], Max[26];
memset(Min, 0, sizeof(Min));
memset(Max, 0, sizeof(Max));
for (int j = b[i].l; j <= b[i].r; j++) {
if (Min[a[j]] == 0) Min[a[j]] = j;
Max[a[j]] = j;
static bool tmp[32];
memset(tmp, false, sizeof(tmp));
for (int k = 0; k <= 25; k++) {
if (Min[k] == 0) continue;
tmp[sum[Min[k]][k] ^ sum[Max[k]][k] ^ sg[b[i].l][k][1] ^ sg[j][k][0]] = true;
}
int ans = 0;
while (tmp[ans] == true) ans++;
sg[j][a[b[i].l - 1]][0] = ans;
}
memset(Min, 0, sizeof(Min));
memset(Max, 0, sizeof(Max));
for (int j = b[i].r; j >= b[i].l; j--) {
if (Max[a[j]] == 0) Max[a[j]] = j;
Min[a[j]] = j;
static bool tmp[32];
memset(tmp, false, sizeof(tmp));
for (int k = 0; k <= 25; k++) {
if (Min[k] == 0) continue;
tmp[sum[Min[k]][k] ^ sum[Max[k]][k] ^ sg[j][k][1] ^ sg[b[i].r][k][0]] = true;
}
int ans = 0;
while (tmp[ans] == true) ans++;
sg[j][a[b[i].r + 1]][1] = ans;
}
while (last < b[i].r) {
last++;
for (int j = 0; j <= 25; j++)
sum[last][j] = sum[last - 1][j];
}
if (a[b[i].l - 1] == a[b[i].r + 1]) {
int tmp = a[b[i].l - 1];
sum[b[i].r][tmp] ^= sg[b[i].l][a[b[i].r + 1]][1];
}
}
for (int i = 1; i <= n; i++) {
memcpy(pre[i], pre[i - 1], sizeof(pre[i - 1]));
pre[i][a[i]] = i;
}
for (int i = 0; i <= 25; i++)
suf[n + 1][i] = n + 1;
for (int i = n; i >= 1; i--) {
memcpy(suf[i], suf[i + 1], sizeof(suf[i + 1]));
suf[i][a[i]] = i;
}
for (int i = 1; i <= q; i++) {
static bool tmp[32];
memset(tmp, false, sizeof(tmp));
int l, r; read(l), read(r);
for (int j = 0; j <= 25; j++) {
if (suf[l][j] > pre[r][j]) continue;
tmp[sum[suf[l][j]][j] ^ sum[pre[r][j]][j] ^ sg[l][j][1] ^ sg[r][j][0]] = true;
}
if (tmp[0]) printf("Alice\n");
else printf("Bob\n");
}
return 0;
}
**【H】**Security
【思路要点】
- 将所有字符串拼接,求得的字符串的后缀数组。
- 对于每一个询问,答案必然是询问串的某一个前缀加上一个字符,枚举这个前缀,在后缀数组上二分出存在这个前缀,并且下一个字符大于询问串的一个区间。
- 用主席树判断这个区间内是否存在一个位置在询问给定的原串区间内,若存在,二分找到答案即可。
- 时间复杂度 。
【代码】
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 300005;
const int MAXP = 6000005;
const int MAXLOG = 20;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct SegmentTree {
struct Node {
int lc, rc;
int cnt;
} a[MAXP];
int n, size, root[MAXN];
void build(int &root, int l, int r) {
root = ++size;
if (l == r) return;
int mid = (l + r) / 2;
build(a[root].lc, l, mid);
build(a[root].rc, mid + 1, r);
}
void init(int x) {
n = x;
size = 0;
build(root[0], 1, n);
}
int copy(int root) {
size++;
a[size] = a[root];
return size;
}
int modify(int root, int l, int r, int pos) {
int ans = copy(root);
a[ans].cnt++;
if (l == r) return ans;
int mid = (l + r) / 2;
if (mid >= pos) a[ans].lc = modify(a[root].lc, l, mid, pos);
else a[ans].rc = modify(a[root].rc, mid + 1, r, pos);
return ans;
}
void extend(int pos, int val) {
if (val == 0) root[pos] = root[pos - 1];
else root[pos] = modify(root[pos - 1], 1, n, val);
}
int query(int rootl, int rootr, int l, int r, int ql, int qr) {
if (l == ql && r == qr) return a[rootr].cnt - a[rootl].cnt;
int mid = (l + r) / 2, ans = 0;
if (mid >= ql) ans += query(a[rootl].lc, a[rootr].lc, l, mid, ql, min(qr, mid));
if (mid + 1 <= qr) ans += query(a[rootl].rc, a[rootr].rc, mid + 1, r, max(mid + 1, ql), qr);
return ans;
}
int query(int l, int r, int ql, int qr) {
if (ql <= qr) return query(root[l - 1], root[r], 1, n, ql, qr);
else return 0;
}
} ST;
struct SuffixArray {
int n, oldn, sa[MAXN], rk[MAXN], height[MAXN], Log[MAXN], Min[MAXN][MAXLOG];
void init(char *s, int old) {
n = strlen(s + 1); oldn = old;
static int x[MAXN], y[MAXN], cnt[MAXN], tmp[MAXN];
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++)
cnt[s[i]]++;
for (int i = 1; i <= 256; i++)
cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--)
sa[cnt[s[i]]--] = i;
rk[sa[1]] = 1;
for (int i = 2; i <= n; i++)
rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]);
for (int k = 1; rk[sa[n]] != n; k <<= 1) {
for (int i = 1; i <= n; i++) {
x[i] = rk[i];
if (i + k <= n) y[i] = rk[i + k];
else y[i] = 0;
}
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++)
cnt[y[i]]++;
for (int i = 1; i <= n; i++)
cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--)
tmp[cnt[y[i]]--] = i;
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++)
cnt[x[i]]++;
for (int i = 1; i <= n; i++)
cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--)
sa[cnt[x[tmp[i]]]--] = tmp[i];
rk[sa[1]] = 1;
for (int i = 2; i <= n; i++)
rk[sa[i]] = rk[sa[i - 1]] + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]);
}
ST.init(oldn);
for (int i = 1; i <= n; i++)
if (sa[i] <= oldn) ST.extend(i, sa[i]);
else ST.extend(i, 0);
for (int i = 1; i <= n; i++) {
height[rk[i]] = height[rk[i - 1]];
if (height[rk[i]]) height[rk[i]]--;
int tmp = sa[rk[i] + 1];
while (s[i + height[rk[i]]] == s[tmp + height[rk[i]]]) height[rk[i]]++;
}
for (int i = 1; i <= n; i++) {
Min[i][0] = height[i];
Log[i] = Log[i - 1];
if (i == (1 << (Log[i] + 1))) Log[i]++;
}
for (int p = 1; p < MAXLOG; p++)
for (int i = 1, j = 1 + (1 << (p - 1)); j <= n; i++, j++)
Min[i][p] = min(Min[i][p - 1], Min[j][p - 1]);
}
int query(int x, int y) {
if (x == y) return n - x + 1;
x = rk[x], y = rk[y];
if (x > y) swap(x, y);
int len = Log[y - x];
return min(Min[x][len], Min[y - (1 << len)][len]);
}
int rmq(int x, int y) {
if (x == y) return n;
if (x > y) swap(x, y);
int len = Log[y - x];
return min(Min[x][len], Min[y - (1 << len)][len]);
}
void getans(char *t, char *s, int st, int len, int ql, int qr) {
int tst = st; st = rk[st];
for (int i = len; i >= 0; i--) {
if (i + 1 > qr - ql + 1) continue;
int l = 1, r = st;
while (l < r) {
int mid = (l + r) / 2;
if (rmq(mid, st) >= i) r = mid;
else l = mid + 1;
}
int pl = l;
l = st, r = n;
while (l < r) {
int mid = (l + r + 1) / 2;
if (rmq(mid, st) >= i) l = mid;
else r = mid - 1;
}
int pr = l;
if (i != len) {
l = pl, r = pr + 1;
while (l < r) {
int mid = (l + r) / 2;
if (s[sa[mid] + i] <= s[tst + i]) l = mid + 1;
else r = mid;
}
pl = l;
}
if (ST.query(pl, pr, ql, qr - i) == 0) continue;
l = pl, r = pr;
while (l < r) {
int mid = (l + r) / 2;
if (ST.query(pl, mid, ql, qr - i) == 0) l = mid + 1;
else r = mid;
}
for (int j = 0; j <= i; j++)
putchar(s[sa[l] + j]);
putchar('\n');
return;
}
printf("-1\n");
}
} SA;
char s[MAXN], t[MAXN];
int tot, n, ql[MAXN], qr[MAXN], len[MAXN], home[MAXN];
int main() {
scanf("%s", s + 1);
tot = n = strlen(s + 1);
int q; read(q);
for (int i = 1; i <= q; i++) {
read(ql[i]), read(qr[i]);
home[i] = tot + 1;
scanf(" %s", t + 1);
len[i] = strlen(t + 1);
for (int j = 1; j <= len[i]; j++)
s[j + tot] = t[j];
tot += len[i];
}
SA.init(s, n);
for (int i = 1; i <= q; i++)
SA.getans(s + home[i] - 1, s, home[i], len[i], ql[i], qr[i]);
return 0;
}