zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 69

    Contest Info


    [Practice Link](https://codeforc.es/contest/1197)
    Solved A B C D E F
    5/6 O O O O Ø -
    • O 在比赛中通过
    • Ø 赛后通过
    • ! 尝试了但是失败了
    • - 没有尝试

    Solutions


    A. DIY Wooden Ladder

    签到题。

    #include <bits/stdc++.h>
    using namespace std;
     
    #define N 100010
    int n, a[N];
     
    int main() {
    	int T; scanf("%d", &T);
    	while (T--) {
    		scanf("%d", &n);
    		for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    		sort(a + 1, a + 1 + n);
    		int res = 0;
    		for (int i = 2; i < n; ++i) {
    			res = max(res, min(a[i] - 1, i - 1));
    		}
    		printf("%d
    ", res);
    	}
    	return 0;
    }
    

    B. Pillars

    题意:
    (n)根柱子,每根柱子上有一个磁盘,一个磁盘能从第(i)根柱子移动到第(j)根柱子的前提是:

    • (j)根柱子上没有磁盘
    • 或者第(i)根柱子上的磁盘半径颜色小于第(j)根柱子的磁盘
      现在给出每根柱子上磁盘的半径,问能够存在一种方案使得将所有的磁盘都移动的一根柱子上

    思路:
    考虑假设能将所有磁盘移动到一根柱子上,那么最终肯定是移动到初始磁盘半径最大的那根柱子上。
    那么考虑从这个柱子开始,每次往两边扩展,取大的贪心往这根柱子上移动。
    如果不能移动就是不行。
    因为考虑最后那根柱子上的磁盘半径肯定是递增的,所以不可能存在跨越移动的情况。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    #define N 200010
    int n, a[N];
     
    int main() {
    	while (scanf("%d", &n) != EOF) {
    		for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    		int pos = 1;
    		for (int i = 2; i <= n; ++i) {
    			if (a[i] > a[pos]) pos = i;
    		}
    		int l = pos - 1, r = pos + 1;
    		bool F = 1;
    		int lst = a[pos];
    		while (l >= 1 || r <= n) {
    			if (l < 1) {
    				if (a[r] >= lst) {
    					F = 0;
    					break;
    				}
    				lst = a[r];
    				++r;
    			} else if (r > n) {
    				if (a[l] >= lst) {
    					F = 0;
    					break;
    				} 
    				lst = a[l];
    				--l;
    			} else {
    				if (a[l] > a[r]) {
    					if (a[l] >= lst) {
    						F = 0;
    						break;
    					}
    					lst = a[l];
    					--l;
    				} else {
    					if (a[r] >= lst) {
    						F = 0;
    						break;
    					}
    					lst = a[r];
    					++r;
    				}
    			}
    		}
    		puts(F ? "YES" : "NO");
    	}
    	return 0;
    }
    

    C. Array Splitting

    题意:
    有一个长度为(n)的序列(a_i),要将序列分成(k)段,使得下式最小:

    [egin{eqnarray*} sumlimits_{i = 1}^k (max(i) - min(i)) end{eqnarray*} ]

    其中(max(i))表示第(i)段中最大的数,(min(i))表示第(i)段中最小的数,保证(a_i)是单调非降序的。

    思路:
    考虑(a_i)是单调非降的,那么(max(i) - min(i))这个东西肯定是区间的末尾减去区间的开头,那么注意到区间的末尾的下一个数即为下一个区间的开头,变换式子有:

    [egin{eqnarray*} a_n - a_1 + sumlimits_{i = 1}^{k - 1} a_{b_i} - a_{b_i + 1} end{eqnarray*} ]

    其中(b_i)表示第(i)段末尾的下标。
    然后这个东西用堆维护一下,贪心取(k - 1)个即可。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    #define ll long long
    #define N 300010
    int n, k, a[N];
     
    int main() {
    	while (scanf("%d%d", &n, &k) != EOF) {
    		for (int i = 1; i <= n; ++i) {
    			scanf("%d", a + i);
    		}
    		ll res = a[n] - a[1]; 
    		vector <int> vec;
    		for (int i = 1; i < n; ++i) {
    			vec.push_back(a[i] - a[i + 1]);
    		}
    		sort(vec.begin(), vec.end());
    		for (int i = 0; i < k - 1; ++i) {
    			res += vec[i];
    		}
    		printf("%lld
    ", res);
    	}
    	return 0;
    }
    

    D. Yet Another Subarray Problem

    题意:
    有一个序列(a_i),定义一个子区间的价值:

    [egin{eqnarray*} sumlimits_{i = l}^r a_i - k leftlceil frac{r - l + 1}{m} ight ceil end{eqnarray*} ]

    问所有子区间中最大的价值是多少?

    思路:
    维护一个前缀和(S),那么变换式子有:

    [egin{eqnarray*} S_r - S_l - k leftlceil frac{r - l}{m} ight ceil end{eqnarray*} ]

    我们希望将(leftlceil frac{r - l}{m} ight ceil)拆开成(leftlceil frac{r}{m} ight ceil - leftlceil frac{l}{m} ight ceil),但是这样是不行的。
    注意到(m)很小,所以可以通过分类讨论一下将(leftlceil frac{x}{m} ight ceil)(x mod m)进行分类,然后讨论一下合并即可。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    #define ll long long
    #define N 300010
    int n, m, k;
    ll a[N];
    ll f[20]; 
     
    int main() {
    	while (scanf("%d%d%d", &n, &m, &k) != EOF) {
    		for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
    		ll res = 0;
    		for (int i = 0; i < m; ++i) f[i] = 0;
    	    f[0] = -k; 	
    		for (int i = 1; i <= n; ++i) {
    			a[i] += a[i - 1]; 
    			for (int j = 0; j < m; ++j) {
    				if (m - i % m >= m - j) {
    					res = max(res, a[i] - f[j] - 1ll * k * (i / m + 1));
    				} else {
    					res = max(res, a[i] - f[j] - 1ll * k * (i / m + 2));
    				}
    			}
    			f[i % m] = min(f[i % m], a[i] - 1ll * k * (i / m + 1));
    		}
    		printf("%lld
    ", res);
    	}
    	return 0;
    }
    

    E. Culture Code

    题意:
    (n)个俄罗斯套娃,每个套娃有内径(in_i)和外径(out_i, out_i > in_i),一个套娃(i)能套在套娃(j)里面,当且仅当(out_i leq in_j)
    定义一组套在的额外空间为

    [egin{eqnarray*} in_1 + (in_2 - out_1) + (in_3 - out_3) + cdots + (in_k - out_{k - 1}) end{eqnarray*} ]

    其中(in_k)为最外成那个套娃的内径。
    定义一组套娃是个极大套娃组:当且仅当不能再有另外一个套娃套在他们身上了。
    询问有多少种极大套娃组的额外空间最小。

    思路:
    先将套娃分成两类:

    • 第一类:没有另外一个套娃能套在它头上
    • 第二类:至少存在一个套娃能够套在它头上

    那么考虑把方案数全都算在第一类套娃头上。
    那么先将所有套娃按内径从小到大排序,那么定义(f[i])表示第(i)个套娃产生最小额外空间时的方案数,那么它能从第(j)个套娃转移过来当且仅当第(out_j <= in_i)
    那么再考虑变换额外空间的式子:

    [egin{eqnarray*} in_k + sumlimits_{i = 1}^{k - 1} out_i - in_i end{eqnarray*} ]

    这样就变成了极大套娃组的额外空间中除了第一个套娃,其他套娃的贡献独立,为(out_i - in_i)
    那么转移的时候维护一下最小额外空间,如果最小额外空间相同就合并方案数,否则更新方案数。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
     
    #define ll long long
    #define INFLL 0x3f3f3f3f3f3f3f3f
    #define N 400010
    #define pii pair <ll, ll>
    #define fi first
    #define se second
    const ll p = 1e9 + 7;
    int n, tot;
    pii a[N]; 
    int b[N];
    ll c[N], d[N];
     
    struct SEG {
    	struct node {
    		ll Min, sum;
    		node() {
    			sum = 0;
    			Min = INFLL;
    		}
    		node(ll Min, ll sum) : Min(Min), sum(sum) {}
    	   	node operator + (const node &other) const {
    			node res = node();
    			if (Min == other.Min) {
    				res.Min = Min;
    				res.sum = (sum + other.sum) % p;
    			} else if (Min < other.Min) {
    				res.Min = Min;
    				res.sum = sum;
    			} else {
    				res.Min = other.Min;
    				res.sum = other.sum;
    			}
    			return res;
    		}	
    	}t[N << 2], res;
    	void build(int id, int l, int r) {
    		t[id] = node();
    		if (l == r) return;
    		int mid = (l + r) >> 1;
    		build(id << 1, l, mid);
    		build(id << 1 | 1, mid + 1, r);
    	}
    	void update(int id, int l, int r, int pos, node x) {
    		if (l == r) {
    			t[id] = t[id] + x;
    			return;
    		}
    		int mid = (l + r) >> 1;
    		if (pos <= mid) update(id << 1, l, mid, pos, x);
    		else update(id << 1 | 1, mid + 1, r, pos, x);
    		t[id] = t[id << 1] + t[id << 1 | 1];
    	}
    	void query(int id, int l, int r, int ql, int qr) {
    		if (l >= ql && r <= qr) {
    			res = res + t[id];
    			return;
    		}
    		int mid = (l + r) >> 1;
    		if (ql <= mid) query(id << 1, l, mid, ql, qr);
    		if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr);
    	}
    }seg;
     
    int id(int x) {
    	return lower_bound(b + 1, b + 1 + tot, x) - b;
    }
     
    int main() {
    	while (scanf("%d", &n) != EOF) {
    		tot = 0; 
    		for (int i = 1; i <= n; ++i) scanf("%lld%lld", &a[i].fi, &a[i].se);
    		for (int i = 1; i <= n; ++i) {
    			b[++tot] = a[i].fi;
    			b[++tot] = a[i].se;
    		}
    		sort(b + 1, b + 1 + tot);
    		tot = unique(b + 1, b + 1 + tot) - b - 1; 
    		sort(a + 1, a + 1 + n, [](pii x, pii y) {
    			if (x.se != y.se) return x.se < y.se;
    			return x.fi > y.fi;		
    		});
    		ll Min = INFLL; 
    		ll res = 0;
    		seg.build(1, 1, tot);
    		for (int i = 1; i <= n; ++i) {
    			seg.res = SEG::node();
    			seg.query(1, 1, tot, 1, id(a[i].se));
    			if (seg.res.Min == INFLL) {
    				seg.res = SEG::node(-a[i].fi + a[i].se, 1);
    				seg.update(1, 1, tot, id(a[i].fi), seg.res);
    				c[i] = a[i].se;
    				d[i] = 1;
    			} else {
    				c[i] = a[i].se + seg.res.Min;
    				d[i] = seg.res.sum;
    				seg.res.Min += -a[i].fi + a[i].se;
    				seg.update(1, 1, tot, id(a[i].fi), seg.res);
    			}
    			if (a[i].fi > a[n].se) {
    				Min = min(Min, c[i]);
    			}
    		}	
    		for (int i = 1; i <= n; ++i) {
    			if (a[i].fi > a[n].se && c[i] == Min) {
    				res = (res + d[i]) % p;
    			}
    		}
    		printf("%lld
    ", res);
    	}
    	return 0;
    }
    
  • 相关阅读:
    归并排序
    CTE 递归
    Cordova 入门文档
    Javascript 原型链
    Windows11 正式版偷渡开启安卓子系统
    快速解决gerrit merge confict问题
    利用VPS来搭建个人主页
    检测串行序列10010
    Verilog语法总结
    深度学习中常见优化算法学习笔记
  • 原文地址:https://www.cnblogs.com/Dup4/p/11229974.html
Copyright © 2011-2022 走看看