Codeforces Round #263 (Div. 2) 解题报告

Source: http://codeforces.com/contest/462

462A  Appleman and Easy Task

好像没什么好说的..

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 int n;
 7 int dx[] = {0, 0, 1, -1},
 8     dy[] = {1, -1, 0, 0};
 9 char map[110][110];
10 int main()
11 {
12     scanf("%d", &n); getchar();
13     for (int i = 0; i < n; i++) cin.getline(map[i], 110);
14     bool flag = true;
15     for (int i = 0; i < n && flag; i++)
16         for (int j = 0; j < n; j++){
17             int x, y, cnt = 0;
18             for (int k = 0; k < 4; k++){
19                 x = i + dx[k], y = j + dy[k];
20                 if (x < 0 || y < 0 || x > n || y > n) continue;
21                 if (map[x][y] == 'o') cnt++;
22             }
23             if (cnt % 2 != 0){
24                 flag = false;
25                 break;
26             }
27         }    
28     if (flag) printf("YES\n");
29     else printf("NO\n");
30     return 0;
31 }
View Code

 

462B  Appleman and Card Game

理解题意后就很简单地贪心

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 char str[101000];
 9 int n, k, ct[30];
10 int main()
11 {
12     memset(ct, 0, sizeof(ct));
13     scanf("%d %d", &n, &k); getchar();
14     gets(str);
15     for (int i = 0; i < n; i++) ct[str[i]-'A'] ++;
16     sort(ct, ct+26);
17     LL ans = 0;
18     for (int i = 25; i >= 0; i--){
19         if (k >= ct[i]){
20             ans = ans + (LL)ct[i] * ct[i];
21             k -= ct[i];
22         }
23         else{
24             ans = ans + (LL)k * k;
25             break;
26         }
27     }
28     cout << ans << endl;
29     return 0;
30 }
View Code

 

461A  Appleman and Toastman

题意:给你一个数的集合,重复两个操作:每次加上集合所有元素的和,如果元素个数大于一,划分为两个集合。问最大分数。

分析:可以倒着考虑,就变成了n个点作为叶子节点,形成一棵二叉树,非叶子节点是两个儿子的和。一种树对应一种答案分数。比较显然的贪心思路就是让最小的元素单独分为一个集合,其余一个集合,这样能让大的数尽量地被多取。但证明不会。。或是可以按题解的思路理解,所有元素乘以-1,所求答案为最小值的相反数,也就是求哈夫曼树。原本哈夫曼树的每次取两个最小元素合并,由于元素为负,所以现在合并完会变为最小元素,再和当前第二小元素合并,也就是同上面一样的做法。算一下每个元素会被取几次就可以了。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 int n;
 9 int a[300100];
10 int main()
11 {
12     scanf("%d", &n);
13     for (int i = 0; i < n; i++) scanf("%d", a+i);
14     sort(a, a+n);
15     LL ans = 0;
16     if (n == 1) ans = a[0];
17     else if (n == 2) ans = (LL)(a[0] + a[1]) * 2ll;
18     else{
19         ans = (LL)(a[n-1] + a[n-2]) * (LL)n;
20         for (int i = n-3, j = n-1; i >= 0; i--, j--){
21             ans = ans + (LL)a[i] * j;
22         }
23     }
24     cout << ans << endl;
25     return 0;
26 }
View Code

 

461B  Appleman and Tree

题意:n个点的树,每个点有黑或白色。对于一颗树,删掉k条边就会形成k+1个子树。现在要删掉一些边,形成许多子树组成的森林,使得每个子树有且只有一个黑点,问有多少种这样的方案。

分析:树形dp。dp[v][0]表示对v为根的子树进行删边,使得新的以v为根的子树,没有黑点,而原本以v为父亲的节点作为根形成的新子树,都是恰有一个黑点的子树,的方案数。dp[v][1]类似前面,不同在于表示v为根恰有一个黑点的方案数。最后答案即dp[root][1]。转移大概是这样:首先dp[v][1] = isblack[v], dp[v][0] = 1 - dp[v][1]。接着对于v的儿子u,u如果不跟着v,dp[v][1] = dp[v][1] * dp[u][1], dp[v][0] = dp[v][0] * dp[u][1], u若跟着v,有dp[v][1]  = dp[v][1] * dp[u][0] + dp[v][0] * dp[u][1], dp[v][0] = dp[v][0] * dp[u][0]。

顺带一提,看了下status里面的代码,发现一种不用双向存边方式,因为这道题给的是pi,表示i和pi之间有边,那么我们可以认为pi是i的父亲,就可以这样存边,给每个pi做个儿子的链表,next[i] = son[pi], son[pi] = i,然后就可以通过v=son[u]进入,v=next[v]遍历访问u的所有儿子v。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 using namespace std;
 5 
 6 const long long mo = 1000000007;
 7 const int maxn = 100000;
 8 struct edge{
 9     int n, v;
10 } e[200100];
11 int n, esize = 0;
12 int en[maxn+10];
13 int b[maxn+10];
14 long long dp[maxn+10][2];
15 void addedge(int u, int v){
16     e[esize].v = v;
17     e[esize].n = en[u];
18     en[u] = esize++;
19 }
20 void dfs(int x, int fa)
21 {
22     dp[x][0] = 1 - b[x];
23     dp[x][1] = b[x];
24     long long dpx0, dpx1;
25     for (int t = en[x]; t != -1; t = e[t].n){
26         int v = e[t].v;
27         if (v == fa) continue;
28         dpx0 = dp[x][0], dpx1 = dp[x][1];
29         dp[x][0] = dp[x][1] = 0;
30         dfs(v, x);
31         dp[x][1] = (dp[x][1] + dp[v][0] * dpx1) % mo;
32         dp[x][1] = (dp[x][1] + dp[v][1] * dpx0) % mo;
33         dp[x][1] = (dp[x][1] + dp[v][1] * dpx1) % mo;
34         dp[x][0] = (dp[x][0] + dp[v][1] * dpx0) % mo;
35         dp[x][0] = (dp[x][0] + dp[v][0] * dpx0) % mo;
36     }
37 }
38 int main()
39 {
40     scanf("%d", &n);
41     int x;
42     memset(en, -1, sizeof(en));
43     for (int i = 1; i < n; i++){
44         scanf("%d", &x);
45         addedge(i, x);
46         addedge(x, i);
47     }
48     for (int i = 0; i < n; i++) scanf("%d", b+i);
49     dfs(0, -1);
50     cout << dp[0][1] << endl;
51     return 0;
52 }
View Code

 

461C  Appleman and a Sheet of Paper

题意:1*10^5的纸,10^5次操作,一种是把当前左侧开始的p个单元的位置翻折到右边,一种是查询当前左侧开始数,l到r的范围内有多少单元长度的纸。

分析:比较机智的题目。。实际上就是对于一个动区间做操作,翻折是把对称点p的一端的一段长度的值加给另一端,查询就是求区间和。给的下标是当前纸的下标,但我们可以记录当前纸的左右端的位置,从而可以维护静态区间(处理了超过右端的问题后,静态区间长度为n)。可以注意到区间长度是不断减小的,一个单位长度只会加给其他地方一次,也就是暴力修改总共也就o(n)(用上树的数据结构是o(nlogn))。为了回答区间和的询问,使用BIT或者SegmentTree。会遇上一个问题就是如果左折得太多,超过了右端,端点就超过n了,这里我们可以转化为翻折右端,然后整个纸面是翻转的(画个图就明白了),搞明白这些就可以写了,麻烦的地方就是下标的细节。。我是没太好的方法,把自己搞晕了,不过还好A了=。=

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 const int maxn = (int)1e5+10;
 7 int n, q, x, y, command, L, R;
 8 int sum[maxn+100];
 9 int lowbit(int x){
10     return x & -x;
11 }
12 void update(int x, int val){
13     for (int i = x; i < maxn; i += lowbit(i))
14         sum[i] += val;
15 }
16 int query(int x){
17     int ret = 0;
18     for (int i = x; i > 0; i -= lowbit(i))
19         ret += sum[i];
20     return ret;
21 }
22 int main()
23 {
24     scanf("%d %d", &n, &q);
25     L = 1, R = n;
26     memset(sum, 0, sizeof(sum));
27     for (int i = 1; i <= n; i++) update(i, 1);
28     for (int i = 0; i < q; i++){
29         scanf("%d", &command);
30         if (command == 1){
31             scanf("%d", &x);
32             int NewL, NewR;
33             if (L < R){
34                 if (x * 2 > R - L + 1){
35                     NewL = L + x - 1, NewR = L;
36                     for (int i = NewL+1, j = NewL; i <= R; i++, j--)
37                         update(j, query(i)-query(i-1));
38                     L = NewL, R = NewR;
39                 }
40                 else{
41                     NewL = L + x;
42                     for (int i = NewL-1, j = NewL; i >= L; i--, j++)
43                         update(j, query(i)-query(i-1));
44                     L = NewL;
45                 }
46             }
47             else{
48                 if (x * 2 > L - R + 1){
49                     NewL = L - x + 1, NewR = L;
50                     for (int i = NewL-1, j = NewL; i >= R; i--, j++)
51                         update(j, query(i)-query(i-1));
52                     L = NewL, R = NewR;
53                 }
54                 else{
55                     NewL = L - x;
56                     for (int i = NewL+1, j = NewL; i <= L; i++, j--)
57                         update(j, query(i)-query(i-1));
58                     L = NewL;
59                 }
60             }
61         }
62         else{
63             scanf("%d %d", &x, &y);
64             if (L < R){
65                 x = L + x - 1, y = L + y - 1;
66                 printf("%d\n", query(y) - query(x));
67             }
68             else{
69                 x = L - x, y = L - y;
70                 printf("%d\n", query(x) - query(y));
71             }
72         }
73     }
74     return 0;
75 }
View Code

 

posted @ 2014-09-04 20:37  james47  阅读(354)  评论(0编辑  收藏  举报