• 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
    ", 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
    ", ret);
    	}
    	return 0;
    }
    

    T3

      显然,这种最小值最大可以二分答案,考虑如何$check$,不妨考虑树形$dp$,设$f[u]$表示在$u$的子树中选一条权值和最大的路径,对于一个子节点$v$,如果$f[v]+dis[u][v]$满足,显然可以选,然后在考虑不满足的情况,显然是选择两条路径拼接在一起,可以用$set$+二分搞(其实容易被卡常)。

    #include <set>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using std::max; using std::min; using std::sort;
    #define file(a) freopen(a".in", "r", stdin); freopen(a".out", "w", stdout);
    typedef long long ll;
    using std::multiset;
    
    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 = 5e4 + 10, Inf = 1e9 + 7;
    int n, m, g, k;
    int cnt, from[N], to[N << 1], dis[N << 1], nxt[N << 1];
    int f[N], st[N]; multiset<int> s; multiset<int>::iterator it;
    int l, r;
    inline void addEdge(int u, int v, int w) {
        to[++cnt] = v, nxt[cnt] = from[u], dis[cnt] = w, from[u] = cnt;
    }
    
    void doit (int u, int fa) {
    	for(int i = from[u]; i; i = nxt[i])
    		if(to[i] != fa) doit(to[i], u);
    	int top = 0;
    	for(int i = from[u]; i; i = nxt[i]) {
    		int v = to[i]; if(v == fa) continue;
    		f[v] += dis[i];
    		if(f[v] >= g) ++k;
    		else st[++top] = f[v];
    	} sort(&st[1], &st[top + 1]), s.clear();
    	for(int i = 1; i <= top; ++i) {
    		it = s.lower_bound(g - st[i]);
    		if(it != s.end()) s.erase(it), ++k;
    		else s.insert(st[i]);
    	}
    	f[u] = s.size() ? *s.rbegin() : 0;
    }
    
    int main () {
        file("track");
        read(n), read(m); 
        for(int i = 1, u, v, w; i < n; ++i) {
    		read(u), read(v), read(w), r += w;
    		addEdge(u, v, w), addEdge(v, u, w);
        } r /= m; int ret = 0;
    	while(l <= r) {
    		g = (l + r) >> 1, k = 0;
    		doit(1, 0);
    		if(k >= m) ret = g, l = g + 1;
    		else r = g - 1;
    	} printf("%d
    ", ret);
        return 0;
    }
    
  • 相关阅读:
    web Function函数
    web语言发展史
    用户正则
    字符串替换
    css单位
    JavaScript DOM&BOM
    css颜色的设置
    pseudo-class与pseudo-element的不同点与相同点
    对css语法中position值的理解
    API
  • 原文地址:https://www.cnblogs.com/water-mi/p/9940555.html
Copyright © 2020-2023  润新知