C. Pearls in a Row
直接贪心做即可。
但是注意如果最后一段不合法,要把最后一段合进上一段,如果整个序列都不合法就无解。
#易错警示:仔细考虑无解条件,可能还有别的坑。
const int MAXN = 3e5 + 10;
int n, aa[MAXN];
int lisan[MAXN], cnt;
int nums[MAXN], tot;
void Add(int x) {
if (nums[aa[x]] == 1) ++tot;
++nums[aa[x]];
}
void Del(int x) {
--nums[aa[x]];
if (nums[aa[x]] == 1) --tot;
}
int main() {
n = read();
rep (i, 1, n) {
aa[i] = read();
lisan[++cnt] = aa[i];
}
std::sort(lisan + 1, lisan + 1 + cnt);
cnt = (int) (std::unique(lisan + 1, lisan + 1 + cnt) - lisan - 1);
int tl = 1;
std::vector<std::pair<int, int> > anss;
rep (i, 1, n) {
aa[i] = (int) (std::lower_bound(lisan + 1, lisan + 1 + cnt, aa[i]) - lisan);
Add(i);
if (tot) {
anss.push_back({tl, i});
while (tl <= i) Del(tl++);
}
}
if (tl != n + 1) {
if (!anss.size()) {
puts("-1");
return 0;
}
anss[anss.size() - 1].se = n;
}
printf("%d\n", (int) anss.size());
for (auto v : anss) printf("%d %d\n", v.fi, v.se);
return 0;
}
D. Professor GukiZ and Two Arrays
只做一次交换的可以直接 \(O(nm)\) 做完。
考虑两次交换的实质是找到一对 \(a_i + a_j\) 和一对 \(b_k + b_l\) 进行交换,于是可以大力把所有的 \(a_i + a_j\) 和 \(b_i + b_j\) 分别求出来,然后二分一下或者双指针扫一扫(类似归并排序的合并)就可以了。
const int MAXN = 2000 + 10;
int n, m;
lli aa[MAXN], bb[MAXN];
lli suma, sumb;
struct S {
lli sum; int a, b;
}; std::vector<S> swpa, swpb;
bool cmp(S x, S y) {
return x.sum < y.sum;
}
std::pair<int, int> swapping[2];
int main() {
n = read(); rep (i, 1, n) suma += (aa[i] = read());
m = read(); rep (i, 1, m) sumb += (bb[i] = read());
rep (i, 1, n) rep (j, i + 1, n) {
swpa.push_back({aa[i] + aa[j], i, j});
} std::sort(ALL(swpa), cmp);
rep (i, 1, m) rep (j, i + 1, m) {
swpb.push_back({bb[i] + bb[j], i, j});
} std::sort(ALL(swpb), cmp);
lli tans = std::abs(suma - sumb);
rep (i, 1, n) {
rep (j, 1, m) {
lli ttans = std::abs(suma - sumb + 2 * bb[j] - 2 * aa[i]);
if (ttans < tans) {
tans = ttans; swapping[0] = {i, j};
}
}
}
int p1 = 0, p2 = 0;
while (p1 < (int) swpa.size() && p2 < (int) swpb.size()) {
lli tmp = suma - sumb + 2 * swpb[p2].sum - 2 * swpa[p1].sum;
if (std::abs(tmp) < tans) {
tans = std::abs(tmp);
swapping[0] = {swpa[p1].a, swpb[p2].a};
swapping[1] = {swpa[p1].b, swpb[p2].b};
}
if (tmp > 0) ++p1;
else ++ p2;
}
printf("%lld\n", tans);
int knds = swapping[1].fi ? 2 : (swapping[0].fi ? 1 : 0);
printf("%d\n", knds);
rep (i, 0, knds - 1) printf("%d %d\n", swapping[i].fi, swapping[i].se);
return 0;
}
E. New Year Tree
线段树维护子树信息板子题。
#易错警示:线段树肌肉记忆真的很容易写错,特别是递归区间那一部分。
const int MAXN = 4e5 + 10;
int n, m;
std::vector<int> G[MAXN];
lli cols[MAXN];
int dfn[MAXN], idx[MAXN], ts; int siz[MAXN];
void dfs(int u, int fa) {
dfn[u] = ++ts; idx[dfn[u]] = u;
siz[u] = 1;
forall (G[u], i) {
int v = G[u][i];
if (v == fa) continue;
dfs(v, u);
siz[u] += siz[v];
}
}
namespace Segt {
lli col[MAXN << 2];
int tag[MAXN << 2];
#define ls (p << 1)
#define rs (p << 1 | 1)
void Update(int p) {
col[p] = col[ls] | col[rs];
}
void buildTree(int p, int l, int r, lli *cc) {
tag[p] = -1;
if (l == r) {
col[p] = (1ll << cc[idx[l]]); return;
} int mid = (l + r) >> 1;
buildTree(ls, l, mid, cc);
buildTree(rs, mid + 1, r, cc);
Update(p);
}
void Mod(int p, int color) {
col[p] = (1ll << color);
tag[p] = color;
}
void Pushdown(int p) {
if (tag[p] == -1) return;
Mod(ls, tag[p]); Mod(rs, tag[p]);
tag[p] = -1;
}
void Modify(int p, int l, int r, int ll, int rr, int color) {
if (l == ll && rr == r) {
Mod(p, color); return;
} Pushdown(p);
int mid = (l + r) >> 1;
if (rr <= mid) Modify(ls, l, mid, ll, rr, color);
else if (mid + 1 <= ll) Modify(rs, mid + 1, r, ll, rr, color);
else {
Modify(ls, l, mid, ll, mid, color);
Modify(rs, mid + 1, r, mid + 1, rr, color);
} Update(p);
}
lli Query(int p, int l, int r, int ll, int rr) {
if (l == ll && rr == r) return col[p];
Pushdown(p);
int mid = (l + r) >> 1;
if (rr <= mid) return Query(ls, l, mid, ll, rr);
else if (mid + 1 <= ll) return Query(rs, mid + 1, r, ll, rr);
else return Query(ls, l, mid, ll, mid) | Query(rs, mid + 1, r, mid + 1, rr);
}
}
int popcount(lli x) {
int r = 0; while (x) { r += (x & 1); x >>= 1; } return r;
}
int main() {
n = read(); m = read();
rep (i, 1, n) cols[i] = read();
rep (i, 1, n - 1) {
int u = read(); int v = read();
G[u].push_back(v); G[v].push_back(u);
} dfs(1, 0);
Segt::buildTree(1, 1, n, cols);
while (m --> 0) {
int t = read();
if (t == 1) {
int v = read(); int c = read();
Segt::Modify(1, 1, n, dfn[v], dfn[v] + siz[v] - 1, c);
} else {
int v = read();
printf("%d\n", (int) popcount(Segt::Query(1, 1, n, dfn[v], dfn[v] + siz[v] - 1)));
}
}
return 0;
}
F. Xors on Segments
考虑每次枚举左端点 \(i\),强制选 \(a_x = a_i\),设 \(f[j]\) 表示 \(a_y\) 在 \(y \in [i + 1, j]\) 取值时 \(f(a_x, a_y)\) 的最大值(类似一个前缀最大值),然后对于每一个询问 \([l, r]\),如果 \(i\) 在这个询问的范围内,就用 \(f[r]\) 更新答案。
const int MAXN = 5e4 + 10;
const int MAXNM = 1e6 + 10;
int n, m;
int aa[MAXN];
int pref[MAXNM];
int f[MAXN];
std::pair<int, int> qrys[MAXN];
int ans[MAXN];
int get(int x, int y) { return pref[y] ^ pref[x - 1]; }
int main() {
std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
rep (i, 1, MAXNM - 5) pref[i] = pref[i - 1] ^ i;
n = read(); m = read();
rep (i, 1, n) {
aa[i] = read();
}
rep (i, 1, m) { qrys[i].fi = read(); qrys[i].se = read(); }
rep (i, 1, n) {
f[i] = aa[i];
rep (j, i + 1, n) f[j] = std::max(f[j - 1], get(std::min(aa[i], aa[j]), std::max(aa[i], aa[j])));
rep (j, 1, m) {
if (qrys[j].fi <= i && i <= qrys[j].se) ans[j] = std::max(ans[j], f[qrys[j].se]);
}
}
rep (i, 1, m) printf("%d\n", ans[i]);
return 0;
}