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

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

 

SYSU出题,感觉变成了手速场。。

HDOJ 4960 Another OCD Patient

题意:一个n<=5000的序列,每个数v[i],可以合并连续的一段加和为一个数,合并一段长度为i的费用为a[i],合并过的不能再合并。问使序列成为对称的序列的最小费用。

分析:显然会是dp。然后并不是很好想= =。。只能yy一些四维五维的dp。这时先贪心地处理一下。考虑两侧,如果不相等,只能向内合并直到相等,这样就可以每次给比较小的一侧多合并一块,直到相等,那么这一大块是至少要合并的一块,递归处理可以得到一个新的已经对称的序列,这时候数的值已经不重要了,只要记录每个块是由多少个数得到的。又想到有对称性,所以其实我们只要对一侧做dp。dp[i]表示前i块和对面的后i块合并的最小费用,dp[i] = min(dp[j] + cost(j+1, i) + cost(对面的j到i)),这就是1d1d的dp,直接2500^2地做。然后我们再枚举i,把dp[i]和对应中间剩下的一段的cost加上,取个最小的。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 
 6 typedef long long LL;
 7 struct node{
 8     int ct, p;
 9 } b[5010];
10 int n;
11 int v[5010], a[5010], dp[5010];
12 bool cmp(node a, node b)
13 {
14     return a.p < b.p;
15 }
16 int main()
17 {
18     while(scanf("%d", &n) && n)
19     {
20         for (int i = 1; i <= n; i++) scanf("%d", v+i);
21         for (int i = 1; i <= n; i++) scanf("%d", a+i);
22         int p1 = 0, p2 = n+1, ct1 = 0, ct2 = 0, num = 0, pos1 = 1, pos2 = n;
23         LL sum1 = 0, sum2 = 0;
24         while(p1 + 1 < p2)
25         {
26             if (sum2 < sum1) sum2 = sum2 + v[--p2], ct2 ++;
27             else sum1 = sum1 + v[++p1], ct1 ++;
28             if (sum1 == sum2){
29                 b[++num].ct = ct1;
30                 b[num].p = pos1 ++;
31                 b[++num].ct = ct2;
32                 b[num].p = pos2 --;
33                 sum1 = sum2 = 0;
34                 ct1 = ct2 = 0;
35             }
36         }
37         //if (sum1 != sum2){
38         if (ct1 || ct2){
39             b[++num].ct = ct1 + ct2;
40             b[num].p = pos1 ++;
41         }
42         sort(b+1, b+1+num, cmp);
43     //    for (int i = 1; i <= num; i++)
44     //        printf("%d %d\n", b[i].ct, b[i].p);
45         dp[0] = 0;
46         for (int i = 1; i * 2 <= num; i++){
47             int sum1 = 0, sum2 = 0;
48             for (int j = i; j > 0; j--){
49                 sum1 += b[j].ct; sum2 += b[num-j+1].ct;
50                 if (j == i) dp[i] = dp[i-1] + a[sum1] + a[sum2];
51                 else dp[i] = min(dp[i], dp[j-1] + a[sum1] + a[sum2]);
52             }
53         }
54         int ans = a[n], sum = 0;
55         for (int i = 1; i <= num/2; i++){
56             sum += b[i].ct + b[num-i+1].ct;
57             ans = min(ans, dp[i] + a[n-sum]);
58         }
59         printf("%d\n", ans);
60     }
61     return 0;
62 }
View Code

 

 

HDOJ 4961 Boring Sum

题意:长度10w的序列,每个数为ai<=10w,相对离它最近且为它的倍数的数有两个,在它前面的为bi,在它后面的为ci,如果没有那就是它本身。问所有数的bi*ci的和。

分析:注意到ai的范围,就有了个o(nsqrt(n))的做法。定义f[i]为一个数的值为i,那么离它最近且为它的倍数的数是多少,初始为i。然后从前往后扫,当前的数为x,那么它的b[i]即f[x],然后再修改它的约数的值即f[j](j|x)。再从后往前做一次得到c[i]。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 typedef long long LL;
 6 const int maxn = 100010;
 7 int n;
 8 int f[maxn+100], a[maxn+100], b[maxn+100];
 9 int main()
10 {
11     while(scanf("%d", &n) && n)
12     {
13         for (int i = 0; i < n; i++) scanf("%d", &a[i]);
14         for (int i = 0; i <= maxn; i++) f[i] = i;
15         for (int i = 0; i < n; i++){
16             int x = a[i];
17             b[i] = f[x];
18             for (int j = 1; j * j <= x; j++)
19                 if (x % j == 0) f[x/j] = f[j] = x;
20         }
21         LL ans = 0;
22         for (int i = 0; i <= maxn; i++) f[i] = i;
23         for (int i = n-1; i >= 0; i--){
24             int x = a[i];
25             ans += (LL)f[x] * b[i];
26             for (int j = 1; j * j <= x; j++)
27                 if (x % j == 0) f[x/j] = f[j] = x;
28         }
29         //printf("%I64d\n", ans);
30         printf("%lld\n", ans);
31     }
32     return 0;
33 }
View Code

 

 

HDOJ 4965 Fast Matrix Calculation

题意:1000*6的矩阵和6*1000的矩阵相乘,然后求他们的一个比较大的幂次,最后统计每位mod6后的和。

分析:智商题。1000*1000的矩阵乘法就跪了,别提矩阵快速幂了。但是猛然发现矩阵乘法有结合性,(AB)^n即A(BA)^(n-1)B,然后就只要做6*6的矩阵快速幂了。。队友写了好几个矩阵乘法,因为大小不同。。晚上补题我就想写个重载就好了,结果一直爆栈。。1000*1000怎么传参都跪,运行就溢出。。无奈开成100*100,在本地调试完改成1000加上hdu的开栈指令勉强过了= =。。还很慢。。

 1 #pragma comment(linker, "/STACK:1024000000,1024000000")  
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 struct mat{
 7     int row, col;
 8     int m[1010][1010];
 9     void clear()
10     {
11         row = col = 0;
12         memset(m, 0, sizeof(m));
13     }
14     void operator =(const mat &p){
15         row = p.row;
16         col = p.col;
17         for (int i = 0; i < row; i++)
18             for (int j = 0; j < col; j++)
19                 m[i][j] = p.m[i][j];
20     }
21     mat operator *(const mat &p);
22 } a, b, c, tmp, sum, ans;
23 
24 mat mat::operator *(const mat &p){
25     ans.row = row;
26     ans.col = p.col;
27     for (int i = 0; i < ans.row; i++)
28         for (int j = 0; j < ans.col; j++)
29             ans.m[i][j] = 0;
30     for (int i = 0; i < row; i++)
31         for (int j = 0; j < col; j++)
32             if (m[i][j]){
33                 for (int k = 0; k < p.col; k++)
34                     ans.m[i][k] += m[i][j] * p.m[j][k];
35             }
36     for (int i = 0; i < row; i++)
37         for (int j = 0; j < p.col; j++)
38             ans.m[i][j] %= 6;
39     return ans;
40 }
41 
42 void print(const mat &tmp)
43 {
44     for (int i = 0; i < tmp.row; i++)
45         for (int j = 0; j < tmp.col; j++)
46             printf("%d%c", tmp.m[i][j], j == tmp.col-1? '\n': ' ');
47 }
48 
49 int n, k;
50 int main()
51 {
52     while(scanf("%d %d", &n, &k) && n != 0 && k != 0)
53     {
54         for (int i = 0; i < n; i++)
55             for (int j = 0; j < k; j++)
56                 scanf("%d", &a.m[i][j]);
57         for (int i = 0; i < k; i++)
58             for (int j = 0; j < n; j++)
59                 scanf("%d", &b.m[i][j]);
60         a.row = n, a.col = k;
61         b.row = k, b.col = n;
62         tmp = b * a;
63     //    print(tmp);
64         sum.row = sum.col = tmp.row;
65         for (int i = 0; i < sum.row; i++)
66             for (int j = 0; j < sum.col; j++)
67                 sum.m[i][j] = 0;
68         for (int i = 0; i < sum.row; i++)
69             sum.m[i][i] = 1;
70         //tmp = mat_powmod(tmp, n*n-1, 6);
71         int exp = n * n - 1;
72         while(exp){
73             if (exp & 1) sum = sum * tmp;
74             exp >>= 1;
75             tmp = tmp * tmp;
76         }
77         c = a * sum * b;
78         int ans = 0;
79         for (int i = 0; i < c.row; i++)
80             for (int j = 0; j < c.col; j++)
81                 ans += c.m[i][j];
82         printf("%d\n", ans);
83     }
84     return 0;
85 }
View Code

 

 

HDOJ 4968 Improving the GPA

题意:给你百分制平均分和课程门数,问gpa最高最低可以是多少。

分析:最多10门课,会想到是枚举搜索一类,正解就是枚举,枚举每个gpa段有几门,就可以算出最高和最低的可能的百分制分数,看给的分数是否在范围内,合法方案取最值即可。不过思考了一下,其实是有贪心策略的。平时考试最讨厌的就是89,84,79这类分数,考到90,85,60啥的感觉很幸运,这个也是同理。比如算最低分,我们先给所有课69分,然后算性价比,发现100分性价比很低,浪费很多分数,所以策略就是尽量多地凑100,剩下分数全给一个69分。最高分同理,先给60,然后都给到85,剩余分数给同一门课(计算发现至少不差)。

 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, ave, n;
15 double gpa[] = {2.0, 2.5, 3.0, 3.5, 4.0};
16 int ct[10];
17 double cal(int ct[])
18 {
19     //for (int i = 0; i < 5; i++)
20     //    printf("%d\n", ct[i]);
21     double ret = 0;
22     for (int i = 0; i < 5; i++)
23         ret = ret + gpa[i] * (double)ct[i];
24     return ret / n;
25 }
26 double calmin(int ave, int tot, int n)
27 {
28     if (ave <= 9) return 2.0000;
29     tot -= 9 * n;
30     memset(ct, 0, sizeof(ct));
31     ct[0] = n;
32     while(tot){
33         ct[0]--;
34         if (tot >= 31){
35             tot -= 31;
36             ct[4] ++;
37         }
38         else{
39             if (tot >= 16) ct[4] ++; 
40             else if (tot >= 11) ct[3] ++;
41             else if (tot >= 6) ct[2] ++;
42             else ct[1] ++;
43             tot = 0;
44         }
45     }
46     return cal(ct);
47 }
48 double calmax(int ave, int tot, int n)
49 {
50     if (ave >= 25) return 4.0000;
51     memset(ct, 0, sizeof(ct));
52     ct[0] = n;
53     while(tot){
54         if (tot > 9) ct[0] --;
55         else {tot = 0; continue;}
56         if (tot < 25){
57             if (tot >= 20) ct[3] ++;
58             else if (tot >= 15) ct[2] ++;
59             else ct[1] ++;
60             tot = 0;
61         }
62         else {tot -= 25; ct[4] ++;}
63     }
64     return cal(ct);
65 }
66 int main()
67 {
68     scanf("%d", &T);
69     while(T--)
70     {
71         scanf("%d %d", &ave, &n);
72         ave -= 60;
73         int tot = ave * n;
74         double ansmin, ansmax;
75         ansmin = calmin(ave, tot, n);
76         ansmax = calmax(ave, tot, n);
77         printf("%.4f %.4f\n", ansmin, ansmax);
78     }
79     return 0;
80 }
View Code

 

 

HDOJ 4969 Just a Joke

题意:女生在半径为R的圆上做匀速圆周运动,男生从圆心开始追,速度大小恒定,保持圆心,男孩女孩为一条直线。男生速度更快,所以是肯定能追上的,但他只能跑D的距离,所以问能不能追到。

分析:大雾没学好啊。。开始以为是阿基米德螺线了,对着那个整了半天一直WA,后来发现阿基米德螺线是保持动射线上的等速(其实速度是变大的),这题是线速度恒定。。所以并不是高数题,而是大雾题。。把男生速度分解,v切^2 + v法^2 = v男^2,因为保持一条直线,所以角速度相同,v切/r = v女生/R,r是径向距离(可以理解成男生当前的半径),所以dr/dt = v法,这几个式子整理一下,把dr和dt分离开,只要做一个三角换元就可以得到t关于r的表达式,代入r=R,得到追上的时刻T,最后V男T和D比较一下就可以了。

 1 #include<cstdio>
 2 #include<cmath>
 3 using namespace std;
 4 
 5 const double eps = 1e-8;
 6 int T;
 7 double v1, v2, r, d;
 8 int main()
 9 {
10     scanf("%d", &T);
11     while(T--)
12     {
13         scanf("%lf%lf%lf%lf", &v1, &v2, &r, &d);
14         if (r*v2*asin(v1/v2)/v1 > d) puts("Why give up treatment");
15         else puts("Wake up to code");
16     }
17     return 0;
18 }
View Code

 

 

HDOJ 4970 Killing Monsters

题意:塔防。每个塔有攻击区间和攻击力,每个怪兽从某位置走到n,有生命值,问最后有多少活着。

分析:上来就当线段树了,区间修改区间查询。结果TLE了,一想其实这个写个o(n)的线段扫描就可以,结果队友敲了个线段扫描,不过是那种配合离散化的,需要排序的。。不过900+ms过了。。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 int n, m;
 6 long long sum[100100];
 7 int ll[100100], dam[100100];
 8 int main()
 9 {
10     while(scanf("%d", &n) && n)
11     {
12         scanf("%d", &m);
13         int l, r, d;
14         memset(ll, 0, sizeof(ll));
15         for (int i = 0; i < m; i++){
16             scanf("%d %d %d", &l, &r, &d);
17             ll[l] += d; ll[r+1] += -d;
18         }
19         dam[0] = 0; sum[n+1] = 0;
20         for (int i = 1; i <= n; i++) dam[i] = dam[i-1] + ll[i];
21         for (int i = n; i >= 1; i--) sum[i] = sum[i+1] + dam[i];
22         int k, ans = 0, pos;
23         long long h;
24         scanf("%d", &k);
25         for (int i = 0; i < k; i++){
26             scanf("%lld %d", &h, &pos);
27             if (sum[pos] < h) ans++;
28         }
29         printf("%d\n", ans);
30     }
31     return 0;
32 }
View Code

 

posted @ 2014-08-20 10:11  james47  阅读(204)  评论(0编辑  收藏  举报