NOIP2018 提高組題解
阿新 • • 發佈:2018-11-10
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
還不會,先放著。