ZAFU 新生赛题解

一篇迟到的题解,本以为大家兴趣不大,但是有同学问了,本人觉得还是应该写一下。

T酱和他的数学题

要求判断末尾有多少个0

我们分析一下就可以知道0只能是来自2 和 5 的乘积。

对于每一个数字我们只需要去判断可以分解出多少个2和5就可以,其中5的出现次数一定会小于2的出现次数。

由于是阶乘的阶乘

所以我们只需要暴力一遍,维护5出现次数的前缀和的前缀和就可以了

 1 #include <stdio.h>
 2 int cal(int x)
 3 {
 4     int res = 0;
 5     while(x)
 6     {
 7         if(x%5) break;
 8         x /= 5;
 9         res++;
10     }
11     return res;
12 }
13 int main()
14 {
15     long long res, tmp;
16     int n;
17     while(scanf("%d", &n) != EOF)
18     {
19         res = tmp = 0;
20         for(int i = 1; i <= n; i++)
21         {
22             tmp += cal(i);
23             res += tmp;
24         }
25         printf("%lld
", res);
26     }
27 }

Vic和骑士

自己手推一下 n = 4 和n = 5的情况, 就会发现规律,只要RB交错排列就可以

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 
 7 int main()
 8 {
 9     int T, n, ca = 0;
10     while(scanf("%d", &n) != EOF)
11     {
12         for(int i = 1; i <= n; i++)
13         {
14             if(i%2) 
15                 for(int j = 1; j <= n; j++)
16                     if(j%2) putchar('B');
17                     else putchar('R');
18             if(i%2==0) 
19                 for(int j = 1; j <= n; j++)
20                     if(j%2) putchar('R');
21                     else putchar('B');
22                     
23             putchar('
');
24         }
25         putchar('
');
26     }
27     
28 }

Vic与水题

根据题目输出就可以, 但是我们提交的输出样例显示有点问题,在题面里也说明了

 1 #include <iostream>
 2 using namespace std;
 3 int main()
 4 {
 5     int T, n, flag;
 6     cout<<"#include<stdio.h>"<<endl;
 7     cout<<"int main()"<<endl;
 8     cout<<"{"<<endl;
 9     cout<<"int n;"<<endl;
10     cout<<"n = 10;"<<endl;
11     cout<<"printf("%d", n);"<<endl;
12     cout<<"return 0;"<<endl;
13     cout<<"}";
14 }

保护牛奶

对于这一题,我们可以发现,牛奶被放成了一圈,并且只能取连续的。

贪心策略是利用圆的对称性来反制对手,所以只有当数量为1 或 2 或 3 时, 先手才赢的机会

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int main()
 5 {
 6     int T, n, flag;
 7     while(scanf("%d", &T) != EOF)
 8     {
 9         while(T--)
10         {
11             scanf("%d", &n);
12             if(n<=3&&n>=1) cout<<"milk will die!!!"<<endl;
13             else cout<<"milk is alive!!!"<<endl;
14         }
15         
16     }
17     
18 }

当老板

这个题目是一个二分题,但是相对于传统的二分,我们需要计算上界或者下界来保证二分的顺利进行.

讲一下二分的思路,主要是check比较难

对于check的值  我们假设是X,每一个员工的可接受的工资是[Li, Ri]

那么对于Li>X 的员工只能是让他排在中位数的右边

对于Ri<X的员工只能是让他排在中位数的左边

然后剩下的我们进行最优分配,如果左边此时还没达到要求人数的一半,那么应该补足左边,这些人发放最小工资,右边的人则是发放X的工资。最后判读这样的分配下花费是否够用可以。

具体看代码吧

 1 #include <stdio.h>
 2 #include <cstring>
 3 #include <vector>
 4 #include <set>
 5 #include <algorithm>
 6 using namespace std;
 7 struct node
 8 { 
 9     int l, r;
10     bool operator < (struct node a)
11     {
12         return l < a.l;
13     }
14 }a[200009];
15 typedef long long ll;
16 int T, n, l, r, mid;
17 ll sum;
18 bool check(int x)
19 {
20     vector <struct node> ve;
21     ve.clear();
22     int num = 0;
23     ll res = 0;
24     for(int i = 1; i <= n; i++)
25         if(a[i].r<x) 
26         {
27             res += a[i].l;
28             num++;
29         }
30         else if(a[i].l>x) 
31         {
32             res += a[i].l;
33         }else ve.push_back(a[i]);
34     
35     if(num>=n/2+1) return false;
36     
37     int size = ve.size();
38     
39     for(int i = 0; i < size; i++)
40     if(num<n/2)
41     {
42         res += ve[i].l;
43         num++;
44     }else res += x;
45     
46     return res <= sum;
47 }
48 
49 int main()
50 {
51     scanf("%d", &T);
52     while(T--)
53     {
54         scanf("%d %lld", &n, &sum);
55         for(int i = 1; i <= n; i++)
56         scanf("%d %d", &a[i].l, &a[i].r);
57     
58         r = 1e9;
59         
60         sort(a+1, a+1+n);
61         l = a[n/2+1].l; 
62     //    printf("%d %d
", l, r);
63         while(l<=r)
64         {
65             mid = (l+r) / 2;
66             if(check(mid))
67             {
68                 if(!check(mid+1)) break;
69                 l = mid+1;
70             }else r = mid - 1;
71         }
72         
73         printf("%d
", mid);
74     }
75 }

滴答滴答

这是一个暴力模拟的题目,具体的只需要按题意敲就可以,但是比较难处理的是涉及到了大数,我们用数组模拟一下就可以了,考虑到新生赛的难度,所以还是比较好处理的

 1 #include <stdio.h>
 2 #include <cstring>
 3 #include <string.h>
 4 const int N = 2e5+10;
 5 char s[N];
 6 int res[N];
 7 int main()
 8 {
 9     int T, len, pos, num, flag;
10     scanf("%d", &T);
11     while(T--)
12     {
13         scanf("%s", s+1);
14         len = strlen(s+1);
15         num = pos = 0;
16         s[len+1] = '*';
17         for(int i = 1; i <= len;)
18         {
19             if(s[i]=='d'&&s[i+1]=='a')
20             {
21                 flag = 0;
22                 i = i + 2;
23                 for(int j = 1; j <= 3 && i <= len; i++, j++)
24                 if(s[i]<='9'&&s[i]>='0')
25                 {
26                     res[++pos] = s[i] - '0';
27                     flag = 1;    
28                 } 
29                 if(flag) num++;
30             }else if(s[i]=='d'&&s[i+1]=='i')
31             {
32                 flag = 0;
33                 i = i + 2;
34                 for(int j = 1; j <= 1 && i <= len; i++, j++)
35                 if(s[i]<='9'&&s[i]>='0')
36                 {
37                     res[++pos] = s[i] - '0';
38                     flag = 1;    
39                 } 
40                 if(flag) num++;
41             }else i++;
42         }
43         res[pos] += num;
44         for(int i = pos; i >= 1; i--)
45         {
46             res[i-1] += res[i] / 10;
47             res[i] %= 10;
48         }
49         flag = 0;
50         for(int i = 0; i <= pos; i++)
51         {
52             if(res[i]) flag = 1;
53             if(flag) printf("%d", res[i]);
54         }
55         if(flag) printf("
");
56         else printf("0
");
57     }
58 }

取石子游戏

这是一个大三学长出的防AK题,只要了解SG函数,那么恭喜你,你离AC只有半步之遥了,由于SG函数的时间复杂度是N^2 , 所以肯定要优化,其实我们观察SG函数可以直接得出一个规律,然后这题就AC了。

 1 #include <stdio.h>
 2 int cal(int x)
 3 {
 4     for(int i = 0; ; i++)
 5         if((1<<i) > x) return i % 3;
 6         
 7 }
 8 int main()
 9 {
10     int T, n, x, res;
11     scanf("%d", &T);
12     while(T--)
13     {
14         scanf("%d", &n); res = 0;
15         for(int i = 1; i <= n; i++)
16         {
17             scanf("%d", &x);
18             res ^= cal(x);
19         }
20         if(res) printf("first
");
21         else printf("second
");
22     }
23 }

题目够简单吗

签到题,读入然后判断数组里面有没有1就可以了

 1 #include <stdio.h>
 2 int main()
 3 {
 4     int T, n, flag;
 5     while(scanf("%d", &T) != EOF)
 6     {
 7         while(T--)
 8         {
 9             scanf("%d", &n);
10             flag = 0;
11             int x;
12             for(int i = 1; i <= n; i++)
13             {
14                 scanf("%d", &x);
15                 if(x) flag = 1;
16             }
17             if(flag) printf("hard
");
18             else printf("easy
");
19         }
20         
21     }
22 }

推恩令

又是来自大三学长的防Ak题,总的思想是二分。

首先我们对题目分析后,我们可以将题目转化为有一个全为0的数列,第i我们可以把数列连续颜色的地方变成i然后问我们有多少种方法可以让原始数列变成输入的数列。

 1 #include <stdio.h>
 2 typedef long long ll;
 3 ll ans[509][509];
 4 const ll MOD = 292929;
 5 int a[509];
 6 void init(int n)
 7 {
 8     for(int i = 1; i <= n; i++)
 9         for(int j = i; j <= n; j++)
10         ans[i][j] = -1;
11 }
12 ll dfs(int l, int r)
13 {
14     if(l>r) return 1;
15     if(ans[l][r]!=-1) return ans[l][r];
16     ll lans = 0, rans = 0;
17     int pos = l, minn = a[l];
18     for(int i = l+1; i <= r; i++)
19     if(a[i] < minn)
20     {
21         minn = a[i]; pos = i;
22     }
23     for(int i = l; i <= pos; i++)
24     lans = (lans + dfs(l,i-1) * dfs(i, pos-1) % MOD) % MOD;
25     
26     for(int i = pos+1; i <= r+1; i++)
27     rans = (rans + dfs(pos+1, i-1) * dfs(i, r) % MOD) % MOD;
28     
29 //    printf("%d %d --- %lld %lld
", l, r, lans * rans + 1);
30     return ans[l][r] = lans * rans % MOD; 
31 }
32 int main()
33 {
34     int T, n;
35     scanf("%d", &T);
36     while(T--)
37     {
38         scanf("%d", &n);
39         init(n);
40         for(int i = 1; i <= n; i++) scanf("%d", a+i);
41         printf("%lld
", dfs(1, n));
42     }
43 }

        

五等分的ACM

题意模拟就可以了

 1 #include <stdio.h>
 2 int cal(int n)
 3 {
 4     int res = 0;
 5     while(n)
 6     {
 7         int tmp = n % 10;
 8         res += tmp * tmp * tmp  * tmp * tmp;
 9         n /= 10;
10     }
11     return res;
12 }
13 int main()
14 {
15     int T, n;
16     scanf("%d", &T);
17     while(T--)
18     {
19         scanf("%d", &n);
20         if(n==cal(n)) printf("YES
");
21         else printf("NO
");
22     }
23 }

找找zyh

分析几个样例

我们定义numz , numy , numh 为以z y h结尾的前缀的当前出现次数

那么最终的答案就是numh

 转移方程也很好理解, 分别是

 当当前字符为  z    时 Numz++;

 当当前字符为  y   时 numy += numz

 当当前字符为  h  时 numh += numy

                                      

字符判断


对于这三个样例  我们可以观察到如果用涂色法去涂这几个字母,A需要两次, B需要三次, C需要一次 (需要全部涂满)。

涂色法可以用dfs处理

需要特殊处理的是,在读入后要在周围添一圈留白,不然可能出现留白需要涂多次的情况,这个我们可以在读入的时候就预留出空间。

 1 #include <stdio.h>
 2 #include <cstring>
 3 char s[50][50] = {0};
 4 int move[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};
 5 void dfs(int x, int y)
 6 {
 7     s[x][y] = '*';
 8     int tx, ty;
 9     for(int i = 0; i < 4; i++)
10     {
11         tx = x + move[i][0];
12         ty = y + move[i][1];
13         if(tx<0 || tx>49 || ty<0 || ty>49) continue;
14         if(s[tx][ty]=='*') continue;
15     //    printf("goto %d %d
", tx, ty);
16         dfs(tx, ty);
17     }
18 }
19 int main()
20 {
21     
22     int time = 0, n;
23     while(gets(s[1]) != NULL)
24     {
25         time = 0;
26         if(strlen(s[1]) == 0) continue;
27         n = 1;
28         for(int i = 2; ; i++)
29         {
30             gets(s[i]);
31             if(strlen(s[i])==0) break;
32             n++;
33         }
34         
35     //    for(int i = 1; i <= n; i++)
36     //    puts(s[i]);
37         
38         for(int i = 0; i <= 49; i++)
39             for(int j = 0; j <= 49; j++)
40             {
41                 if(s[i][j]!='*') 
42                 {
43                     dfs(i, j);
44                     time++;
45                 }
46             }
47             
48     //    printf("%d
", time);
49             
50         if(time==1) printf("C
");
51         else if(time==2) printf("A
");
52         else printf("B
");
53             
54         memset(s, 0, sizeof(s));
55     }
56     return 0;
57 }

足球经理

利用二维数组读入后,我们对于每一个队暴力模拟,计算分数就可以了

计算完分数判断最大值,然后看有没有两个队分数一样就可以

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 int res[120][120];
 6 struct node
 7 {
 8     int score, id;
 9     bool operator < (struct node a)
10     {
11         return score > a.score;
12     }
13 }a[120];
14 
15 int main()
16 {
17     int T, n;
18     while(scanf("%d", &T) != EOF)
19     {
20         while(T--)
21         {
22             scanf("%d", &n);
23             for(int i = 1; i <= n; i++)
24                 for(int j = 1; j <= n; j++)
25                 scanf("%d", &res[i][j]);
26             
27             for(int i = 1; i <= n; i++)
28             {
29                 a[i].id = i;
30                 a[i].score = 0;
31                 for(int j = 1; j <= n; j++)
32                 {
33                     if(i==j) continue;
34                     if(res[i][j] > res[j][i]) a[i].score += 3;
35                     else if(res[i][j]==res[j][i]) a[i].score += 1;
36                 }
37             }
38             a[n+1].score = -1;
39             sort(a+1, a+1+n);
40             if(a[1].score != a[2].score) printf("%d
", a[1].id);
41             else printf("yep!
");
42         }
43     }
44     
45 }

        

原文地址:https://www.cnblogs.com/loenvom/p/12036606.html