2014 Multi-University Training Contest 6 部分题目解题报告

http://acm.hdu.edu.cn/search.php?field=problem&key=2014%20Multi-University%20Training%20Contest%206&source=1&searchmode=source

HDOJ 4921 Map

题意:

模型转化为:给不超过10条链,每条链不超过1000个点,每条链可以选前缀的一段或是不选,每一种选法都会得到分数,分数又分成两步计算(还挺麻烦,不解释了),现在让你随便选,问选出来的分数期望是多少。

分析:

每种选法是等概率的,所以所求期望就是 Σ每个方案的分数/总方案数。总方案数就是所有链的长度+1的乘积,再-1(去掉都不选的情况)。考虑每种方案的分数很复杂,其实可以考虑每个点的分数会被取多少次。对于第一步,每个点就是自己的分数乘以会包含该点的方案数,即是其他链的取法的乘积乘上该点到该链末尾的长度。然后第二步就是考虑每一个等级(1000),然后2^10暴力枚举每条链该等级选或不选,累加即可。题解没仔细看,貌似更很厉害些,还有个log的复杂度=。=。。。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 int T, n, m, x, y, tot;
 6 int nex[10100], v[10100], line[15][1010];
 7 bool head[10100];
 8 double sum, sumv;
 9 void dfs(int lv, int pos, int yi, int sumi, int xi, double sum)
10 {
11     if (pos > tot){
12         if (yi > 1){
13             sumv += (double)yi * sumi / xi * sum;
14         }
15         return;
16     }
17     if (line[pos][0] < lv) 
18         dfs(lv, pos+1, yi, sumi, xi, sum*(line[pos][0]+1));
19     else{
20         dfs(lv, pos+1, yi+1, sumi+v[line[pos][lv]], xi, sum*(line[pos][0]-lv+1));
21         dfs(lv, pos+1, yi, sumi, xi, sum*lv);
22     }
23 }
24 int main()
25 {
26     scanf("%d", &T);
27     while(T--)
28     {
29         scanf("%d %d", &n, &m);
30         for (int i = 0; i < n; i++) scanf("%d", v+i);
31         memset(head, 1, sizeof(head));
32         memset(nex, -1, sizeof(nex));
33         for (int i = 0; i < m; i++){
34             scanf("%d %d", &x, &y);
35             nex[x] = y;
36             head[y] = 0;
37         }
38         tot = 0; sum = 1.0; sumv = 0;
39         for (int i = 0; i < n; i++) if (head[i]){
40             line[++tot][0] = 1;
41             line[tot][1] = i;
42             int now = i; int &cnt = line[tot][0];
43             while (nex[now] != -1){
44                 now = nex[now];
45                 line[tot][++cnt] = now;
46             }
47         }
48         for (int i = 1; i <= tot; i++)
49             sum *= (double)(line[i][0] + 1);
50         for (int i = 1; i <= tot; i++){
51             int len = line[i][0];
52             for (int j = 1; j <= len; j++){
53                 int k = line[i][j];
54                 if (tot == 1) sumv += (double)v[k] * (len - j + 1);
55                 else sumv += (double)v[k] * sum * (len - j + 1) / (len+1);
56             }
57         }
58 //        printf("%.3lf\n", sumv);
59         if (tot > 1){
60             int maxlv = 1;
61             for (int i = 1; i <= tot; i++)
62                 if (line[i][0] > maxlv) maxlv = line[i][0];
63             for (int i = 1; i <= maxlv; i++){
64                 int xi = 0;
65                 for (int j = 1; j <= tot; j++)
66                     if (line[j][0] >= i) xi++;
67                 dfs(i, 1, 0, 0, xi, 1.0); 
68             }
69         }
70 //        printf("%.3lf\n", sumv);
71         printf("%.3lf\n", sumv/(sum-1.0));
72     }
73     return 0;
74 }

 

 

HDOJ 4923 Room and Moor

题意:

给定序列Ai,只有0和1,你要选择一个序列Bi,满足单调非递减,每一项范围在[0,1]的条件,使得Σ(Ai - Bi)^2最小。

分析:

先考虑贪心,前面连续的0和末尾连续的1都可以去掉,每当碰上0,不用上升,如果是1,考虑上升和上升的高度,然后就跪了。可以想象最后的Bi应该是一条条高度上升的线段,也就是从l到r的一段区间,Bl..Bi..Br的值相同,对所求式子求导,或是直接展开成一个二次函数,会发现对于一段区间,Bi的最优值等于Ai中对应区间的1的个数除以1和0的个数(即区间长度)。通过这样的方式来贪心处理不了100...000111...110这种情况。其实可以引入单调栈让我们有“反悔”的机会。单调栈里存的是之前的区间的Bi的大小,显然是递增的。我们加入新的区间(开始就是新的点),计算它的Bi最优值,如果比栈顶的高,我们就加入栈。如果比栈顶低,我们就把栈顶区间取出,和当前区间合并,再和新栈顶比较,直到比某个栈顶高或者栈空,将合并后的区间压入栈。最后扫描一遍栈求出答案即可。

 1 #include<cstdio>
 2 using namespace std;
 3 
 4 const int maxn = (int)1e5+10;
 5 struct node{
 6     int l, r;
 7     double h;
 8     node(){}
 9     node(int ll, int rr, double hh):l(ll), r(rr), h(hh){}
10 } stack[maxn];
11 int T, n;
12 int a[maxn], sum[maxn];
13 int main()
14 {
15     scanf("%d", &T);
16     while(T--)
17     {
18         scanf("%d", &n);
19         sum[0] = 0;
20         for (int i = 1; i <= n; i++){
21             scanf("%d", a+i);
22             sum[i] = sum[i-1] + a[i];
23         }
24         int top = 0;
25         for (int i = 1; i <= n; i++){
26             node now = node(i, i, sum[i]-sum[i-1]);
27             while(top > 0 && now.h < stack[top-1].h){ 
28                 top --;
29                 now.l = stack[top].l;
30                 now.h = (double)(sum[now.r] - sum[now.l-1])/(now.r - now.l + 1);
31             }
32             stack[top++] = now;
33         }
34         double ans = 0;
35         for (int i = 0; i < top; i++){
36             int ll = stack[i].l, rr = stack[i].r;
37             int len = rr - ll + 1;
38             double hh = stack[i].h;
39             int tot = sum[rr] - sum[ll-1];
40             ans += hh * hh * len - 2.0 * tot * hh + tot;
41         }
42         printf("%.6lf\n", ans);
43     }
44     return 0;
45 }

 

 

HDOJ 4925 Apple Tree

题意:

n×m的图,每格可以种苹果或施肥,种苹果得一个苹果,施肥的格子上下左右的格子若种苹果则苹果数翻倍,问最多能得几个苹果。

分析:

本场签到题,观察发现黑白染色可以获得最多的苹果。范围很小,可以直接染色求结果,推公式也很快。(提示:先考虑1×m的情况,再考虑2×m的情况,推广到n×m的情况)

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 int T, n, m;
 7 int main()
 8 {
 9     scanf("%d", &T);
10     while(T--)
11     {
12         scanf("%d %d", &n, &m);
13         if (n > m) swap(n, m);
14         if (m == 1){
15             puts("1");
16             continue;
17         }
18         if (n == 1) printf("%d\n", 2 * (m-1));
19         else printf("%d\n", 8 * (m-1) * (n-1));
20     }
21     return 0;
22 }
View Code

 

 

HDOJ 4927 Series 1

题意:

给一个n个数的数列,相邻项做差得到新的数列(听说叫差分序列?),做n-1次得到一个数,问这个数是多少。

分析:

做差时不计算,而是保留所有数字,可以发现原来数列每个数会乘上一个系数,刚好是杨辉三角上的一行,再加上正负号,累加就是答案。组合数会到C(1500, 3000)的规模,所以需要高精度。代码是队友写的。

 1 import java.io.*;
 2 import java.math.*;
 3 import java.util.*;
 4 
 5 public class Main{
 6     public static void main(String [] args){
 7             Scanner cin;
 8             cin = new Scanner(System.in);
 9             int T, n;
10             BigInteger ans, C, sum[] = new BigInteger [3100];
11             T = cin.nextInt();
12             while (T>0){
13                 T--;
14                 n = cin.nextInt();
15                 for (int i=0;i<n;i++)    sum[i] = cin.nextBigInteger();
16                 C = BigInteger.ONE;
17                 ans = BigInteger.ZERO;
18                 int w;
19                 if (n%2==1) w = 0;
20                 else w = 1;
21                 for (int i=0;i<n;i++){
22                     if (i%2==w)    ans = ans.add(sum[i].multiply(C));
23                     else ans = ans.subtract(sum[i].multiply(C));
24                     C = C.multiply(BigInteger.valueOf(n-i-1)).divide(BigInteger.valueOf(i+1));
25                 }
26                 System.out.println(ans);
27             }
28             cin.close();
29         }
30 }
View Code

 

 

HDOJ 4930 Fighting the Landlords

题意:

简化版斗地主,给出双方的牌,现在你先手,问有没有一种出牌方法使得对方在这一轮管不住你,或是直接出完。

分析:

先判断能否出完,然后处理大小王和炸弹的情况,接下来一项项匹配即可,只要有一项能大过对方就满足要求。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<cstdlib>
  7 #include<set>
  8 #include<map>
  9 #include<queue>
 10 #include<ctime>
 11 #include<string>
 12 using namespace std;
 13 
 14 int T;
 15 char aa[30], bb[30];
 16 int a[20], b[20];
 17 int abig[10], bbig[10], acnt[10], bcnt[10];
 18 int trans(char x)
 19 {
 20     if ('3' <= x && x <= '9') return x - '0' - 2;
 21     if (x == 'T') return 8;
 22     if (x == 'J') return 9;
 23     if (x == 'Q') return 10;
 24     if (x == 'K') return 11;
 25     if (x == 'A') return 12;
 26     if (x == '2') return 13;
 27     if (x == 'X') return 14;
 28     if (x == 'Y') return 15;
 29 }
 30 bool legal()
 31 {
 32     memset(acnt, 0, sizeof(acnt));
 33     int sum = 0;
 34     for (int i = 1; i <= 15; i++)
 35         sum += a[i];
 36 //    printf("%d\n", sum);
 37     if (sum > 6) return false;
 38     if (sum == 1) return true;
 39     for (int i = 1; i <= 15; i++){
 40         if (a[i]) acnt[a[i]]++;
 41     }
 42 //    for (int i = 1; i <= 15; i++)
 43 //        printf("%d ", a[i]);
 44 //    printf("\n");
 45 //    for (int i = 1; i <= 6; i++)
 46 //        printf("%d ", acnt[i]);
 47 //    printf("\n");
 48     if (sum == 2 && acnt[2]) return true;
 49     if (sum == 3 && acnt[3]) return true;
 50     if (sum == 4 && acnt[4]) return true;
 51     if (sum == 4 && acnt[3]) return true;
 52     if (sum == 5 && acnt[3] && acnt[2]) return true;
 53     if (sum == 6 && acnt[4]) return true;
 54     return false;
 55 }
 56 bool comp()
 57 {
 58     if (legal()) return 1;
 59     if (a[14] && a[15]) return 1;
 60     if (b[14] && b[15]) return 0;
 61     memset(abig, -1, sizeof(abig));
 62     memset(acnt, 0, sizeof(acnt));
 63     memset(bbig, -1, sizeof(bbig));
 64     memset(bcnt, 0, sizeof(bcnt));
 65     for (int i = 1; i <= 15; i++){
 66         if (a[i]){
 67             acnt[a[i]]++;
 68             for (int j = 1; j <= a[i]; j++)
 69                 abig[j] = i;
 70         }
 71         if (b[i]){
 72             bcnt[b[i]]++;
 73             for (int j = 1; j <= b[i]; j++)
 74                 bbig[j] = i;
 75         }
 76     }
 77     if (abig[4] > bbig[4]) return true;
 78     else if (bbig[4] != -1) return false;
 79     if (abig[1] > bbig[1]) return true;
 80     if (abig[2] > bbig[2]) return true;
 81     if (abig[3] > bbig[3]) return true;
 82     if (abig[3] != -1 && acnt[1] != 0 && bcnt[1] == 0 && bcnt[2] == 0) return true;
 83     if (abig[3] != -1 && acnt[2] != 0 && bcnt[2] == 0) return true;
 84     return false;
 85 }
 86 int main()
 87 {
 88     scanf("%d", &T);
 89     while(T--)
 90     {
 91         scanf("%s %s", aa, bb);
 92         memset(a, 0, sizeof(a));
 93         memset(b, 0, sizeof(b));
 94         for (int i = 0; i < strlen(aa); i++){
 95             int x = trans(aa[i]);
 96             a[x] ++;
 97         }
 98         for (int i = 0; i < strlen(bb); i++){
 99             int x = trans(bb[i]);
100             b[x] ++;
101         }
102         bool win = comp();
103         if (win) puts("Yes");
104         else puts("No");
105     }
106     return 0;
107 }
View Code

 

posted @ 2014-08-08 19:50  james47  阅读(189)  评论(0编辑  收藏  举报