Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2) DEF题解

好久没写cf题解了,这场cf写了两个线段树,其中还有一个竟然写搓了,导致没能上橙,5555,看来省赛前是注定不能橙了。
D. Restore Permutation

解法:我们从小到大枚举 i i i,然后从线段树中找到最右边的一个0的位置 k k k然后 a [ k ] = i a[k]=i a[k]=i,然后我们清空这个值,然后在线段树中将区间 [ k + 1 , r ] [k +1, r] [k+1,r]减去 i i i即可, 线段树入门题
#include
#define ll long long
using namespace std;
const int maxn = 2e5 + 10;
ll mn[maxn * 4], tag[maxn * 4], p[maxn * 4], a[maxn];
#define ls o * 2
#define rs o * 2 + 1
#define m (l + r) / 2
void pushup(int o) {
    if (mn[ls] < mn[rs])
        mn[o] = mn[ls], p[o] = p[ls];
    else
        mn[o] = mn[rs], p[o] = p[rs];
}
void pushdown(int o) {
    tag[ls] += tag[o]; tag[rs] += tag[o];
    mn[ls] += tag[o]; mn[rs] += tag[o];
    tag[o] = 0;
}
void build(int o, int l, int r) {
    if (l == r) {
        scanf("%lld", &mn[o]);
        p[o] = l;
        return;
    }
    build(ls, l, m);
    build(rs, m + 1, r);
    pushup(o);
}
void up(int o, int l, int r, int ql, int qr, ll v) {
    if (ql > qr)
        return;
    if (l >= ql && r <= qr) {
        mn[o] += v;
        tag[o] += v;
        return;
    }
    pushdown(o);
    if (ql <= m)
        up(ls, l, m, ql, qr, v);
    if (qr > m)
        up(rs, m + 1, r, ql, qr, v);
    pushup(o);
}
int main() {
    int n;
    scanf("%d", &n);
    build(1, 1, n);
    for (int i = 1; i <= n; i++) {
        a[p[1]] = i;
        int k = p[1];
        up(1, 1, n, k, k, 1e11);
        up(1, 1, n, k + 1, n, -i);
    }
    for (int i = 1; i <= n; i++)
        printf("%lld ", a[i]);
}

E. Let Them Slide

线段树暴力解法:每一行的第 i i i个元素 x i x_{i} xi,我们用线段树在区间 [ i , n − l e n + i ] [i, n - len + i] [i,nlen+i]去和 x i x_{i} xi取max,每一行处理完后,在线段树中暴力更新这一行数对答案的贡献,暴力更新直到某个区间最大值等于最小值就不用继续往下更新了,线段树骚操作题
#include
#define ll long long
using namespace std;
const int maxn = 1e6 + 10, inf = 1e9 + 1;
int mn[maxn * 4], tag[maxn * 4], mx[maxn * 4], vis[maxn * 4];
ll sum[maxn * 4];
vector<int> G;
#define ls o * 2
#define rs o * 2 + 1
#define m (l + r) / 2
void pushup(int o) {
    mx[o] = max(mx[ls], mx[rs]);
    mn[o] = min(mn[ls], mn[rs]);
}
void add(int o) {
    if (!vis[o])
        G.push_back(o);
    vis[o] = 1;
}
void pushdown(int o) {
    if (tag[o] != -inf) {
        tag[ls] = max(tag[o], tag[ls]);
        tag[rs] = max(tag[o], tag[rs]);
        mn[ls] = max(mn[ls], tag[o]);
        mn[rs] = max(mn[rs], tag[o]);
        mx[ls] = max(mx[ls], tag[o]);
        mx[rs] = max(mx[rs], tag[o]);
        add(ls);
        add(rs);
        tag[o] = -inf;
    }
}
void up(int o, int l, int r, int ql, int qr, int v) {
    if (ql > qr || mn[o] >= v)
        return;
    add(o);
    if (l >= ql && r <= qr) {
        if (mx[o] <= v) {
            mx[o] = mn[o] = tag[o] = v;
            return;
        }
        if (mn[o] >= v)
            return;
        pushdown(o);
        if (mn[ls] < v)
            up(ls, l, m, ql, qr, v);
        if (mn[rs] < v)
            up(rs, m + 1, r, ql, qr, v);
        pushup(o);
        return;
    }
    pushdown(o);
    if (ql <= m)
        up(ls, l, m, ql, qr, v);
    if (qr > m)
        up(rs, m + 1, r, ql, qr, v);
    pushup(o);
}
ll qu(int o, int l, int r, int k) {
    if (l == r)
        return sum[o];
    ll ans = sum[o];
    if (k <= m)
        ans += qu(ls, l, m, k);
    else
        ans += qu(rs, m + 1, r, k);
    return ans;
}
void gao(int o, int l, int r) {
    if (mx[o] == mn[o]) {
        sum[o] += mx[o];
        return;
    }
    gao(ls, l, m);
    gao(rs, m + 1, r);
}
int main() {
    int q, n, l, x;
    scanf("%d%d", &q, &n);
    for (int i = 1; i <= 4 * n; i++)
        mx[i] = mn[i] = tag[i] = -inf;
    for (int i = 1; i <= q; i++) {
        scanf("%d", &l);
        for (int j = 1; j <= l; j++) {
            scanf("%d", &x);
            up(1, 1, n, j, n - (l - j), x);
        }
        if (l < n) {
            up(1, 1, n, l + 1, n, 0);
            up(1, 1, n, 1, n - l, 0);
        }
        gao(1, 1, n);
        for (auto o : G)
            mx[o] = mn[o] = tag[o] = -inf, vis[o] = 0;
        G.clear();
    }
    for (int i = 1; i <= n; i++)
        printf("%lld ", qu(1, 1, n, i));
}

F. Bits And Pieces

解法:我们从后面往前面枚举 a i a_{i} ai对答案的贡献,我们用一个 c n t cnt cnt存所有 i i i后面的数的所有子集出现次数,然后从高位枚举 a i a_{i} ai二进制为0的位置 j j j,贪心的用 c n t cnt cnt判断其是否出现了超过或等于2次,如果出现次数管够,我们贪心的将 a i ∣ ( 1 < < j ) ai|(1<<j) ai(1<<j),最后和答案取max即可
#include
using namespace std;
const int maxn = 1 << 21;
int cnt[maxn], vis[maxn], a[maxn];
void insert(int x, int id) {
    if (cnt[x] == 2 || vis[x] == id)
        return;
    cnt[x]++;
    vis[x] = id;
    for (int i = 20; ~i; i--)
        if (x >> i & 1)
            insert(x ^ (1 << i), id);
}
int main() {
    int n, ans = 0;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    int m = maxn - 1;
    for (int i = n; i; i--) {
        int x = m ^ a[i];
        int s = 0;
        for (int j = 20; ~j; j--)
        if (x >> j & 1) {
            if (cnt[s | (1 << j)] == 2)
                s |= (1 << j);
        }
        if (i <= n - 2)
            ans = max(ans, a[i] | s);
        insert(a[i], i);
    }
    printf("%d\n", ans);
}

你可能感兴趣的:(数据结构----线段树)