CodeForces ZeptoLab Code Rush 2015
拖了好久的题解,想想还是补一下吧。
A. King of Thieves
直接枚举起点和5个点之间的间距,进行判断即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 char s[110]; 5 6 int main() 7 { 8 //freopen("in.txt", "r", stdin); 9 10 int n; 11 bool ans = false; 12 scanf("%d%s", &n, s); 13 for(int q = 0; q < n && !ans; q++) if(s[q] == '*') 14 for(int l = 1; q + 4*l < n; l++) 15 { 16 int i; 17 for(i = 1; i <= 4; i++) if(s[q + i*l] == '.') break; 18 if(i > 4) { ans = true; break; } 19 } 20 21 printf("%s\n", ans ? "yes" : "no"); 22 23 return 0; 24 }
B. Om Nom and Dark Park (DFS)
给一个完全二叉树,增加若干条边的权值,使得从根到每个叶节点的权值之和相同。
直接从下往上维护这颗树,累加所增加的权值。
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int maxn = (1 << 12) + 10; 5 LL a[maxn], num[maxn], ans, sum[maxn]; 6 7 int n; 8 9 void dfs(int o, int d) 10 { 11 if(d > n) return; 12 int lc = o*2, rc = o*2+1; 13 dfs(lc, d+1); dfs(rc, d+1); 14 int m = max(sum[lc] + a[lc], sum[rc] + a[rc]); 15 ans += m - (sum[lc] + a[lc]); 16 ans += m - (sum[rc] + a[rc]); 17 sum[o] = m; 18 } 19 20 int main() 21 { 22 //freopen("in.txt", "r", stdin); 23 24 scanf("%d", &n); 25 for(int i = 2; i < (1<<(n+1)); i++) scanf("%I64d", &a[i]); 26 dfs(1, 1); 27 printf("%I64d\n", ans); 28 29 return 0; 30 }
C. Om Nom and Candies (部分贪心)
关于部分贪心可以看高逸涵的09年国家队论文。
这道题可以分成两种情况,用不同的办法来解决。
- 有一种糖果的质量大于等于1000,那么我们枚举这种糖果的数量来求最大值。根据题目数据范围,循环的次数不会超过109 / 103 = 106.
- 两种糖果的质量都小于1000,然后选一个性价比比较高的糖果。不妨设,那么我们说蓝色糖果的数量一定小于Wr。这样贪心的理由就是,如果有Wr个蓝色糖果,我们完全可以换成Wb个红色糖果。因为这种方案的糖果质量是一样的,但是总价值是HbWr < HrWb。所以我们可以枚举蓝色糖果的数量,来求最优解,并且循环的次数不超过1000.
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 5 6 int main() 7 { 8 LL c, hr, hb, wr, wb, ans = 0; 9 scanf("%I64d%I64d%I64d%I64d%I64d", &c, &hr, &hb, &wr, &wb); 10 const int M = 1000; 11 if(wb > wr) { swap(hr, hb); swap(wr, wb); } 12 if(wr > M) 13 { 14 for(LL i = 0; i * wr <= c; i++) 15 { 16 LL j = (c - i * wr) / wb; 17 ans = max(ans, (LL)i*hr + j*hb); 18 } 19 cout << ans << endl; 20 return 0; 21 } 22 23 if(hb*wr > hr*wb) { swap(hr, hb); swap(wr, wb); } 24 for(LL i = 0; i <= wr && i * wb <= c; i++) 25 { 26 LL j = (c - i * wb) / wr; 27 ans = max(ans, i*hb+j*hr); 28 } 29 cout << ans << endl; 30 31 return 0; 32 }
D. Om Nom and Necklace (KMP)
题解转自:http://cyberzhg.github.io/blog/Online-Judge/CF526ABCDE/
这道题最终还是并没有看明白 r mod k是怎么来的,(⊙﹏⊙)b
1 #include <cstdio> 2 3 const int maxn = 1000000 + 10; 4 char s[maxn]; 5 int f[maxn], m, k; 6 7 void getFail() 8 { 9 f[0] = 0, f[1] = 0; 10 for(int i = 1; i < m; i++) 11 { 12 int j = f[i]; 13 while(j && s[i] != s[j]) j = f[j]; 14 f[i+1] = s[i] == s[j] ? j+1 : 0; 15 } 16 } 17 18 int main() 19 { 20 //freopen("in.txt", "r", stdin); 21 scanf("%d%d%s", &m, &k, s); 22 getFail(); 23 24 for(int i = 1; i <= m; i++) 25 { 26 int l = i - f[i]; 27 int R = i / l; 28 if(i % l == 0) 29 printf("%c", R / k >= R % k ? '1' : '0'); 30 else 31 printf("%c", R / k > R % k ? '1' : '0'); 32 } 33 printf("\n"); 34 35 return 0; 36 }
E. Transmitting Levels
在CF上发现了一种非常神的解法。首先预处理一下前缀和,然后用一个链表head[i]记录以第i个节点为结尾的,每段上的数之和不超过b的第一个数的下标。当这个跨度大于等于n的时候就说明找到答案了。
len就是记录分成了多少段。
我说的可能不太清楚,但是代码十分好懂,而且十分简练。感觉比官方题解要好很多。
但是我不太能证明它的正确性,为什么一旦找到i - head[i] >= n就输出答案了呢,也就是为什么后面不可能有最优解了呢?
1 #include <cstdio> 2 3 typedef long long LL; 4 const int maxn = 2000000 + 10; 5 LL a[maxn], sum[maxn], len[maxn], head[maxn]; 6 7 int main() 8 { 9 //freopen("in.txt", "r", stdin); 10 11 int n, q; 12 scanf("%d%d", &n, &q); 13 for(int i = 1; i <= n; i++) { scanf("%I64d", &a[i]); a[i+n] = a[i]; } 14 for(int i = 1; i <= n * 2; i++) { sum[i] = sum[i-1] + a[i]; head[i] = i; } 15 while(q--) 16 { 17 int b; scanf("%d", &b); 18 for(int i = n + 1, j = 1; i <= n * 2; i++) 19 { 20 while(sum[i] - sum[j] > b) j++; 21 head[i] = head[j]; 22 len[i] = len[j] + 1; 23 if(i - head[i] >= n) { printf("%I64d\n", len[i]); break; } 24 } 25 } 26 27 return 0; 28 }