zoukankan      html  css  js  c++  java
  • Codeforces Round #631 (Div. 1) A-C

    ( ext{Div. 2/3}) 混了一个多月后,四个号终于都上紫了,也没用理由不打 ( ext{Div. 1}) 了。这是我人生中的第一场 ( ext{Div .1}) ,之前也没用刻意的刷过题。在赛场上 ( ext{A}) 在 WA 了 (3) 次后终于过了,然后 (35 ext{min}) 的时候过了 ( ext{B}),想了一个小时 ( ext{C}) 未果,耻辱下线。事后上了 (60) 分,大概是安慰吧......

    A. Dreamoon Likes Coloring

    比较显然地想到贪心,让左端点从左到右的顺序放线段,每次最少给上次的颜色留出一个位置即可。

    然后比较坑的是,这题你放的左端点必须保证右端点 (le n),所以贪心地考虑就是让每条线段尽量往左放,但是你又不知道往左放后面会不会不够。所以记录一个 (d = n - m)。初始设定每个颜色包含的块时 (1)(d) 维护的就是当前还需要扩展的块数。然后逆向思维,从右往左考虑,尽量让左端点往左边放即可,但是不能超过 (d),否则把别的颜色本该有的地方给占领了。

    几个无解的判断:

    • (sum l < n) 肯定有空白

    • (l_m > n - (m - 1)) 最后一个覆盖了太多,导致容不下 (m) 个颜色

    • 放的过程中发现右端点势必 (> n)

    还有一些细节,具体看代码

    时间复杂度 (O(n))

    #include <iostream>
    #include <cstdio>
     
    using namespace std;
     
    const int N = 100005;
     
    int n, m, l[N], ans[N];
     
    typedef long long LL;
     
    LL s = 0;
     
    int main() {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= m; i++) scanf("%d", l + i), s += l[i];
    	if (s < n) {
    		puts("-1");
    		return 0;
    	}
    	if (l[m] > n - (m - 1)) {
    		puts("-1");
    		return 0;
    	}
    	
    	int j = n - l[m] + 1;
    	int d = (j - 1) - (m - 1);
    	ans[m] = j;
    	for (int i = m - 1; i; i--) {
    		j -= min(d + 1, l[i]) ;
    		d -= min(d + 1, l[i]) - 1;
    		if (j + l[i] - 1 > n) {
    			puts("-1");
    			return 0;
    		}
    		ans[i] = j;
    	}
    	for (int i = 1; i <= m; i++) printf("%d ", ans[i]);
    	return 0;
    }
    

    B. Dreamoon Likes Sequences

    既然要保证异或和、值本身都上升的序列。那么不妨从二进制的角度去考虑:

    • 要让值上升,意味着 (a_{2}) 在二进制下最高的 (1) 大于等于 (a_{1}) 的位置,否则 (a_{i - 1}) 就大了
    • 异或和上升,意味着 (a_2) 在二进制下最高的 (1) 必须大于 (a_1) 的位置,否则异或和就把最高的那处一消掉了,就不能保证上升了。

    然后通过数学归纳法,我们能知道 (a_i)(b_i) 二进制意义下最高的 (1) 位置是一样的,否则被消掉了肯定不能保证异或和上升。

    所以若 (a_{i + 1}) 能接上 (a_i),当且仅当 (a_{i + 1}) 二进制下最高的 (1)(a_i) 大。

    由于不涉及序列长度以及具体数值,只关心最后一个数二进制下最高的 (1) 的位置,那么这样设计 ( ext{DP}) 状态:

    • (f_i) 表示一段序列,最后一个数在二进制下最高的 (1) 在第 (i) 位的方案数。

    考虑状态转移,(f_j (j > i) gets f_i * ext{w(j)})( ext{w(j)}) 表示在二进制下最高位是 (1),且不超过 (d) 的数字的数量,这个很好搞,下界就是 (2^j),上界就是 (min(2^{j+1}-1, d))

    注意初始化,第一个元素可以为 ([1, d]) 的任意值。

    时间复杂度 (O(Tlog^2 10^9))

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
     
    typedef long long LL;
     
    const int S = 31;
     
    int d, P, f[S];
     
    int main() {
    	int T; scanf("%d", &T);
    	while (T--) {
    		memset(f, 0, sizeof f);
    		scanf("%d%d", &d, &P);
    		for (int i = 0; i < S; i++) {
    			if ((1 << i) > d) continue;
    			int c = min((LL)d, (1ll << (i + 1)) - 1) - (1 << i) + 1;
    			f[i] = c;
    		}
    		for (int i = 0; i < S; i++) {
    			if (f[i] == 0) continue;
    			for (int j = i + 1; j < S; j++) {
    				if ((1 << j) > d) continue;
    				int c = min((LL)d, (1ll << (j + 1)) - 1) - (1 << j) + 1;
    				f[j] = (f[j] + (LL)c * f[i]) % P;
    			}
    		}
    		int ans = 0;
    		for (int i = 0; i < S; i++) (ans += f[i]) %= P;
    		printf("%d
    ", ans); 
    	}
    	return 0;
    }
    

    C. Drazil Likes Heap

    考虑每一次的删数操作,实际上是将一个叶子节点的位置去除了,我们要保证堆的结构仍然为满二叉树,删除总和最大的一些数。我们可以预处理出来删除每个节点,最终会去除的叶子节点的位置的高度,设这个东西为 (maxDep_i)

    我们发现,删除一个数是否合法的条件为:(maxDep_i > g)。否则会打破深度限制。

    然后贪心策略是:从小到大的顺序依次考虑每一层,能删则删,循环直到不能删为止。为啥这样是对的呢?首先我们的策略应该是:尽可能删合法的大的,因为假设你不删大的,那一定还得删另外一个数,来消除本应该这个大的数消除的叶子位置,这样顶替的肯定不优,所以能删则删。

    还有个性质,就是说不同子树之间删除互不影响,这个比较显然,因为自己删除只会改变自己的子树。所以根据堆的性质,我们只需要从小到大考虑每一层能不能删即可。可以理解为当考虑删除当前位置的节点时,权重更大的能消除这个位置的(就是当前节点的祖先)已经考虑过了,所以这种枚举方式是正确的。

    代码实现起来,就是模拟一个堆,然后删除的过程中不要忘了维护 (maxDep) 信息,每次操作是 (O(dep)) 的,所以总复杂度是 (O(T2 ^ hh)) 的。

    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    
    const int N = 3000005;
    
    typedef long long LL;
    
    int h, g, a[N], dep[N], maxDep[N], adj[N], tot;
    
    LL ans;
    
    void inline pushup(int p) {
    	maxDep[p] = max(dep[p], a[p << 1] > a[p << 1 | 1] ? maxDep[p << 1] : maxDep[p << 1 | 1]);
    }
    
    void del(int p) {
    	a[p] = 0;
    	if (!a[p << 1] && !a[p << 1 | 1]) { maxDep[p] = 0; return; }
    	else if (!a[p << 1]) a[p] = a[p << 1 | 1], del(p << 1 | 1);
    	else if (!a[p << 1 | 1]) a[p] = a[p << 1], del(p << 1);
    	else {
    		int t = a[p << 1] > a[p << 1 | 1] ? p << 1 : p << 1 | 1;
    		a[p] = a[t], del(t);
    	}
    	pushup(p);
    }
    
    int main() {
    	int T; scanf("%d", &T);
    	while (T--) {
    		ans = tot = 0;
    		scanf("%d%d", &h, &g);
    		for (int i = 1; i < (1 << h); i++) {
    			scanf("%d", a + i);
    			a[i << 1] = a[i << 1 | 1] = 0;
    			dep[i] = dep[i >> 1] + 1;
    			ans += a[i];
    		}
    		for (int i = (1 << h) - 1; i; i--) pushup(i);
    		for (int i = 1; i < (1 << h); i++) 
    			while (a[i] && maxDep[i] > g) {
    				ans -= a[i], adj[++tot] = i, del(i);
    			}
    		printf("%lld
    ", ans);
    		for (int i = 1; i <= tot; i++) printf("%d " , adj[i]);
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    递归
    数据结构与算法-复杂度分析
    'latin-1' codec can't encode characters in position解决字符问题
    redis理解
    spring 嵌套事务问题
    Parameterized testing with any Python test framework
    分布式事务测试考虑点
    python orm / 表与model相互转换
    Python多线程、多进程
    JS运动
  • 原文地址:https://www.cnblogs.com/dmoransky/p/12641117.html
Copyright © 2011-2022 走看看