NOIP2018 提高组题解

Day1

T1

  据说是原题积木大赛,但是考场上蠢了,只会写数据结构,于是写了一个线段树\(+\)\(+\)贪心,先选出最小的,然后区间修改,然后把左右两端区间的最小值丢进堆里,不停从堆中去最小值更新即可(模拟题)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using std::pop_heap; using std::push_heap;
using std::greater; using std::min;
#define file(a) freopen(a".in", "r", stdin); freopen(a".out", "w", stdout);
typedef long long ll;

template <typename T>
inline void read(T &x) {
    x = 0; char ch = getchar(); int f = 1;
    while(ch < '0' || ch > '9') { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    x *= f;
}

const int N = 1e5 + 10, LogN = 20, Inf = 1e9 + 7;
int n, d[N];
struct Range {
    int minval, site, l, r;
    inline bool operator < (const Range &a) const {
        return minval < a.minval;
    }
    inline bool operator > (const Range &a) const {
        return minval > a.minval;
    }
};
Range val[N << 2]; int add[N << 2];
struct Heap {
    Range h[N]; int siz;
    void push(Range x) { h[++siz] = x, push_heap(&h[1], &h[siz + 1], greater<Range>()); }
    void pop() { pop_heap(&h[1], &h[siz + 1], greater<Range>()), --siz; }
    inline bool empty() { return siz == 0; }
    inline int size() { return siz; }
    inline Range top() { return h[1]; }
}q;
inline void pushup(int o, int lc, int rc) {
    val[o] = min(val[lc], val[rc]);
}
inline void pushdown(int o, int lc, int rc) {
    if(add[o]) {
        val[lc].minval += add[o], val[rc].minval += add[o];
        add[lc] += add[o], add[rc] += add[o], add[o] = 0;
    }
}
void build(int o = 1, int l = 1, int r = n) {
    if(l == r) { val[o] = (Range){d[l], l, l, l}; return ; }
    int mid = (l + r) >> 1, lc = o << 1, rc = lc | 1;
    build(lc, l, mid), build(rc, mid + 1, r), pushup(o, lc, rc);
}
void modify(int ml, int mr, int k, int o = 1, int l = 1, int r = n) {
    if(ml > mr) return ;
    if(l >= ml && r <= mr) {
        val[o].minval += k, add[o] += k;
        return ;
    } int mid = (l + r) >> 1, lc = o << 1, rc = lc | 1;
    pushdown(o, lc, rc);
    if(ml <= mid) modify(ml, mr, k, lc, l, mid);
    if(mr > mid) modify(ml, mr, k, rc, mid + 1, r);
    pushup(o, lc, rc);
}
Range query(int ml, int mr, int o = 1, int l = 1, int r = n) {
    if(ml > mr) return (Range){Inf,0,0,0};
    if(l >= ml && r <= mr) return val[o];
    int mid = (l + r) >> 1, lc = o << 1, rc = lc | 1; Range val = (Range){Inf,0,0,0};
    pushdown(o, lc, rc);
    if(ml <= mid) val = query(ml, mr, lc, l, mid);
    if(mr > mid) val = min(val, query(ml, mr, rc, mid + 1, r));
    return val;
}

int main () {
    file("road");
    read(n);
    for(int i = 1; i <= n; ++i) read(d[i]);
    build();
    int tmpn = n, ret = 0; Range now = val[1];
    now.l = 1, now.r = n; q.push(now);
    while(tmpn && q.size()) {
        now = q.top(), q.pop(); --tmpn;
        ret += now.minval;
        modify(now.l, now.r, -now.minval);
        Range l = query(now.l, now.site - 1), r = query(now.site + 1, now.r);
        l.l = now.l, l.r = now.site - 1, r.l = now.site + 1, r.r = now.r;
        q.push(l), q.push(r);
    } printf("%d\n", ret);
    return 0;
}

T2

  不难发现,两个硬币系统是等价的当且仅当其中的某些硬币能被除自己以外的硬币凑出来。完全背包强制不选自己就行了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using std::max; using std::sort;
#define file(a) freopen(a".in", "r", stdin); freopen(a".out", "w", stdout);
typedef long long ll;

template <typename T>
inline void read(T &x) {
    x = 0; char ch = getchar(); int f = 1;
    while(ch < '0' || ch > '9') { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
    x *= f;
}

const int N = 1e2 + 10, M = 2.5e4 + 10;
int t, n, a[N], f[M];

int main () {
    file("money");
    read(t);
    while(t--) {
        read(n); int ret = 0, m = 0;
        for(int i = 1; i <= n; ++i) read(a[i]), m = max(m, a[i]);
        memset(f, 0, sizeof f), sort(&a[1], &a[n + 1]);
        for(int i = 1; i <= n; ++i) {
            for(int j = a[i] + 1; j <= m; ++j)
                f[j] |= f[j - a[i]];
            if(!f[a[i]]) {
                ++ret, f[a[i]] = 1;
                for(int j = a[i]; j <= m; ++j)
                    f[j] |= f[j - a[i]];
            }
        }
        printf("%d\n", ret);
    }
    return 0;
}

T3

还不会,先放着。

猜你喜欢

转载自www.cnblogs.com/water-mi/p/9940555.html