Educational Codeforces Round 6 部分题解

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;
}
posted @ 2021-10-21 16:14  Handwer  阅读(38)  评论(0编辑  收藏  举报