新生初赛题目、解题思路、参考代码一览

1001. 无聊的日常

Problem Description

两位小朋友小A和小B无聊时玩了个游戏,在限定时间内说出一排数字,那边说出的数大就赢,你的工作是帮他们统计他们获胜的次数。

Input

第一行是一个T,代表游戏的次数T(T≤1000)。每组两个整数p,y(1≤p≤\(10^{100}\),1≤y≤\(10^{100}\)),分别表示两位小朋友说出的数字。

Output

输出两个数,A和B获胜的次数。后面没有换行,仅此一题

Sample Input

  1. 2
  2. 11 22
  3. 22 11

Sample Output

  1. 1 1

解题思路

  • 签到题,但是要上天的出题人给的数据不对,造成了大量的WA。后来我点进去一看数据长度不对,放到python里len一下,数据竟然有70多位,才让大佬改成了\(10^{100}\)。
  • 很简单,比较两个数字的大小并统计次数就好了。但是数字非常大,你需要用一个字符串存储。
  • 字符串大数比较,先比较长度,长度相同再从最高位向低位比较每一位的大小(直至不等)。
  • 应当注意,既然没说相等的情况,当然不应该把相等的情况也算进去。
  • 时间复杂度:O(N),常数为3以上。
  • 空间复杂度:O(N)

参考代码

  • ?:的语法是gnu c/c++独有,a ?: c等价于a ? (a) : c
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main() {
  4. int T, resP = 0, resY = 0, temp;
  5. char p[99]={0}, y[99]={0};
  6. scanf("%d", &T);
  7. while (T--) {
  8. scanf("%s%s", p, y);
  9. temp = (int) strlen(p) - (int) strlen(y) ?: strcmp(p, y);
  10. if (temp > 0) ++resP;
  11. else if (temp < 0) ++resY;
  12. }
  13. printf("%d %d", resP, resY);
  14. return 0;
  15. }

### 1002. 写个编译器

Problem Description

写一个编译器,支持下列语言之一:

C语言 https://en.wikipedia.org/wiki/C_(language)

保证只有main函数

只使用int char main printf关键字,除字符串外只有" ( ) + - * = , 数字 字母 { } ; 空格 换行 的出现,且没有undefined behavior

保证没有判断、循环、递归



int main() {

char t;

t = 70;

printf ("%cello", t+=2);

}

Python语言 https://en.wikipedia.org/wiki/Python_(programming_language)

只使用print end关键字,除字符串外只有" ( ) + - * = , 数字 字母 空格 换行 的出现

保证没有判断、循环、递归



print (1, 4, end="+11=25")

"输出14+11=25,后面没有换行"

print (1, 4, "+11=25")

"输出14+11=25,后面有换行"

"没有print语句的表达式不会被输出"

JScript https://en.wikipedia.org/wiki/JScript

只使用WScript.Echo WScript.StdOut.Write关键字,除字符串外只有" ( ) + - * = , 数字 字母 ; 空格 . 的出现(JScript的换行比较麻烦,暂且避开)

保证没有判断、循环、递归,所有变量为数字或字符串



x=3;WScript.Echo(x+6);WScript.Stdout.Write("1\n2");

Text语言 http://esolangs.org/wiki/Text

除字符串外只有" ( ) + - * 数字 字母 空格 换行 , { }

保证没有循环、递归



Output Character H

Let t Be 3

Output String ell

If t-2 Positive {

Output Character With Ansii 111

}

If t-4 Positive {

Output Character With Ansii 10

}

BrainFuck http://esolangs.org/wiki/Brainfuck

保证只有> < + - . [ ]

每个位置可以储存0-255的数字,超过将会绕回

保证执行次数小于100 000

Input

依次输入几种语言的代码,两种语言之间以==========(10个等号)分割。保证所有代码可以正常编译执行

Output

选择一种进行编译并输出运行结果。

Sample Input

  1. int main() {
  2. char t;
  3. t = 70;
  4. printf ("%cello", t+=2);
  5. }
  6. ==========
  7. print (1, 4, end="+11=25")
  8. "输出14+11=25,后面没有换行"
  9. print (1, 4, "+11=25")
  10. "输出14+11=25,后面有换行"
  11. "没有print语句的表达式不会被输出"
  12. ==========
  13. x=3;WScript.Echo(x+6);WScript.Stdout.Write("1\n2");
  14. ==========
  15. Output Character H
  16. Let t Be 3
  17. Output String ell
  18. If t-2 Positive {
  19. Output Character With Ansii 111
  20. }
  21. If t-4 Positive {
  22. Output Character With Ansii 10
  23. }
  24. ==========
  25. +++++[>++++++++<-]>.

Sample Output

  1. Hello

解题思路

  • 又是大佬的神作,看看就好。
  • 向真的写了BrainFuck的巨巨们Orz一个。
  • 注意倒数第二种语言Text,输出就是源代码本身。
  • 时间复杂度:O(N),常数大概在1~3
  • 空间复杂度:O(N)

参考代码

  1. #include <stdio.h>
  2. #include <string.h>
  3. char code[(int) (3e7)], *result = code;
  4. int main() {
  5. fread(code, 1, (int) (3e7), stdin);
  6. for (int i = 0; i < 3; ++i)
  7. result = strstr(result, "\n==========\n") + 12;
  8. *strstr(result, "\n==========\n") = 0;
  9. printf("%s", result);
  10. return 0;
  11. }

### 1003. 手机密码

Problem Description

某天CZJ在玩辣鸡游戏,玩着玩着发现手机被hack了,重启之后发现界面上出现了一个数字和一个密码框。

CZJ试了半天发现,当输入的正整数满足以下条件时,界面上不会弹出可恶的错误:

  1. 它的二进制形式中1的数量和显示数字的(二进制中1数量)相同;
  2. 十进制大于显示的数字;
  3. 在所有可行答案中最小。

可悲的是又跳出一个数字。CZJ算了几个之后觉得好麻烦,你可以帮他算一下吗?

Input

第一行是一个T,代表数据的组数T(T≤100001)。接下来是T行,每行有一个数字N,表示显示的数字(1≤n≤1000000000)。

Output

针对每一个数字输出对应密码。

Sample Input

  1. 2
  2. 1
  3. 5

Sample Output

  1. 2
  2. 6

Hint

1的二进制表达为1,2的二进制表达为10,5的是101,6的是110

解题思路

  • 首先只考虑第一个条件。统计数字N的二进制表示中,“1”的数量C。然后输出一个数X,使得X的二进制表示是由C个连续的“1”构成。
  • 再考虑第二个条件,要求输出的X要比N大。那就从右边找极值,从右到左扫描N的二进制,先扫过一段连续的“0”,再扫过一段连续的“1”,之后再第一次扫描到“0”时停止。如N=92(0101 1100)。把这一位0置为1,下一位1置为0(为了保证“1”的个数不变),像这样:X=108(0110 1100)。这样输出X的确要比原数N大。
  • 最后考虑第三个条件,在所有可行方案中最小。我们再次考察X=108(0110 1100),由于X=108具有N=88所没有的更高位的“1”,所以X一定比N大,而不论后面低位的数字如何变化。于是重排X=99(0110 0011),也就是将比变换位低位的1全部移到右边去。
  • 时间复杂度:O(d)=O(logN)
  • 空间复杂度:O(1)

参考代码1

  • __builtin_ctz函数是gnu c/c++独有,用于计算从右起第一个“1”之后的“0”的个数,参数为0时是ub;
  • 也可以用__builtin_ffs函数找到从右起第一个“1”的位置;
  • 然而这些函数看看就好,记得可以用,还是推荐自己算。
  1. #include <stdio.h>
  2. int main() {
  3. int T, N, a, b, t;
  4. scanf("%d", &T);
  5. while (T--) {
  6. scanf("%d", &N);
  7. t = a = N & -N;
  8. do a <<= 1; while (a & N);
  9. b = (N & (~(-a))) >> (__builtin_ctz(t) + 1);
  10. N = N & -a ^ a ^ b;
  11. printf("%d\n", N);
  12. }
  13. return 0;
  14. }

参考代码2

  1. #include <stdio.h>
  2. int main() {
  3. int T, N, a, b;
  4. scanf("%d", &T);
  5. while (T--) {
  6. scanf("%d", &N);
  7. a = 0, b = 0;
  8. while (!(N & 1)) N >>= 1, ++a;
  9. while (N & 1) N >>= 1, ++b;
  10. printf("%d\n", ((N | 1) << (a + b)) & (~((1 << (a + b)) - 1)) | ((1 << (b - 1)) - 1));
  11. }
  12. return 0;
  13. }

### 1004. Zyj大逃亡

Problem Description

Zyj打比赛时又没做出题来,还写崩了一道题,他的队友们和SCNU的大佬们都跑来追杀他啦!Zyj在逃亡过程中和大佬们一起穿越到了LOL世界中。大佬们启动了“疾走”技能,很快就把Zyj围堵到了一个包围圈中。

Zyj拥有一个被动技能,当不向敌人走动时能获得飞一般的速度,从而逃出包围圈。已知包围圈的半径为\(R\),可以设Zyj所在位置为原点O,大佬们都恰好站在圆上一点,并知道每位大佬的坐标位置为\(P_i(x_i, y_i)\)。显然每位大佬之间都隔着一段圆弧,Zyj可以且仅可以从长度\(L \geqslant \frac12 R\)的圆弧的中点逃出以保证他的被动技能生效。若不存在这样的圆弧(即长度都太短)则Zyj无路可逃。

注意,如果Zyj的可选最长圆弧不唯一,即使长度足够,也认为Zyj无论走哪个方向都不背向敌人,Zyj还是无路可逃。

Input

第一行只有一个正整数\(T\)(\(T \leqslant 1000\)),代表了\(T\)个情形。

接下来有\(T\)组输入,每组输入中包含两个正整数\(N\)(\(N>3\))和\(R\)(\(0<R<1000\)),分别代表包围圈的半径和追杀Zyj的大佬数量。接下来\(N\)行每行有\(2\)个数字,分别代表\(N\)个大佬的坐标\((x_i, y_i)\),保证所有的\(x_i^2+y_i^2=R^2\)成立,提供数据的精度保证7位有效小数(舍入后)。保证数据读得完。

Output

对于每个Zyj逃亡的情形,如果Zyj能逃出来,则输出Zyj可逃离的最长圆弧的中点的坐标(保证答案唯一,且保留4位小数);如果Zyj无路可逃,请输出"Zyj has been slain"(不包括双引号)。

Sample Input

  1. 2
  2. 4 25
  3. 0 25
  4. -25 0
  5. -0 -25
  6. 25 -0
  7. 4 25
  8. 0 25
  9. 0 -25
  10. 20 15
  11. 20 -15

Sample Output

  1. Zyj has been slain
  2. -25.0000 0.0000

解题思路

  • 我出的题。题目有修改,“如果Zyj的可选最长圆弧不唯一”原本的描述是“如果Zyj能选择的所有圆弧都等长”,但是多事的oyk说看不懂,结果改了描述后好像变难了。我的代码仍然是“所有等长”的版本,如果要判“最长不唯一”,要另开数组标记一下浮点数。
  • 如果用笛卡尔坐标系,不是不能做,挺难的。考虑极坐标系,由于坐标都在圆上,长度均等于R。每个大佬的直角坐标对应极坐标角度angle,对angle进行排序,两两相减得到每段圆弧所对圆心角theta,遍历一遍这个theta,最后有答案就除以2再转直角坐标。
  • 注意由于是个,还要算上第一个和最后一个坐标之间的圆弧。
  • 正确姿势做法:极角排序
  • 奇怪的错误点:
    1. 完全不会测试浮点数,判断相等时有精度问题;
    2. 完全不会比较浮点数,判断大小时比较错误。
  • 时间复杂度:O(NlogN)

参考代码

  • C语言在stdlib.h里有个qsort排序函数
  • C++在algorithm中(STL)有个std::sort排序函数
  • 不要干手写冒泡排序这种奇怪的事情,手写快排也不要
  • 浮点数由于精度误差,不能直接比较相等,要设置一个较小的允许误差,小于这个误差可认为等于0
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <math.h>
  4. #include <stdlib.h>
  5. //做自己出的题WA了是一种怎样的体验?Zyj的回答,获得0个赞同。
  6. const double PI = acos(-1.);
  7. int angleCount;
  8. double angleArray[10000];
  9. void init() {
  10. angleCount = 0;
  11. memset(angleArray, 0x0, sizeof(angleArray));
  12. }
  13. int N, R;
  14. void read() {
  15. double x, y;
  16. scanf("%d%d", &N, &R);
  17. for (int i = 0; i < N; ++i) {
  18. scanf("%lf%lf", &x, &y);
  19. angleArray[angleCount++] = atan2(y, x);
  20. }
  21. }
  22. int fcmp(double a, double b) {
  23. /*if (fabs(a - b) <= 1e-7)*/
  24. if (fabs(a - b) / fabs(a) <= 1e-7 ||
  25. fabs(a - b) / fabs(b) <= 1e-7)
  26. return 0;
  27. if (a < b) return -1;
  28. /*if (a > b)*/ return 1;
  29. }
  30. int compareDouble(const void *a, const void *b) {
  31. return fcmp(*(double *) a, *(double *) b);
  32. }
  33. void work() {
  34. qsort(angleArray, (size_t) angleCount, sizeof(double), compareDouble);
  35. angleArray[angleCount++] = angleArray[0] + 2. * PI;
  36. int isNotSame = 0;
  37. int rangeUpperIdx = 1;
  38. double maxRange = angleArray[rangeUpperIdx] - angleArray[rangeUpperIdx - 1];
  39. for (int i = 2; i < angleCount; ++i) {
  40. double temp = angleArray[i] - angleArray[i - 1];
  41. int cmpJudge = fcmp(temp, maxRange);
  42. if (cmpJudge) ++isNotSame;
  43. if (cmpJudge > 0) {
  44. maxRange = temp;
  45. rangeUpperIdx = i;
  46. }
  47. }
  48. if (isNotSame && fcmp(maxRange, 0.5) > 0) {
  49. double theta = (angleArray[rangeUpperIdx] + angleArray[rangeUpperIdx - 1]) / 2.;
  50. printf("%.4f %.4f\n", R * cos(theta), R * sin(theta));
  51. } else {
  52. printf("Zyj has been slain\n");
  53. }
  54. }
  55. int main() {
  56. int T;
  57. scanf("%d", &T);
  58. while (T--) {
  59. init();
  60. read();
  61. work();
  62. }
  63. return 0;
  64. }

### 1005. Czj数数

Problem Description

自从Czj上了初中,已经不再满足于从1数到100了,他打算来个高难度的挑战。

设a1=1,a2=11,有数列ai,任意1<i≤n,元素ai−1与其自身的最后一位数字组成了新的数字X(如233将会变成2333),而ai是X的最大质因子。由于Czj很懒,数到2333就觉得无聊而跑去写gc了。现在请你帮他完成这未竟的事业。

Input

第一行是一个正整数T(T<100),代表接下来有T次询问。

接下来的T行,每一行输入一个正整数i(1≤i≤104)。

Output

对于每次询问,你应该求出数组中的第i个元素ai,并以"Case #t: ai"的格式单独输出一行,不包括双引号。其中#t代表询问的编号。

Sample Input

  1. 3
  2. 5
  3. 6
  4. 7

Sample Output

  1. Case #1: 23
  2. Case #2: 233
  3. Case #3: 2333

解题思路

  • 还是本菜鸡出的题。
  • 遇事不决打个表。如果你打了表,你就会发现生活的美好——这里面原来是有循环节的。从第5个数开始,到第30个数,是一个循环节。
  • 一般这种一眼看过去是递推,又没找到公式,干脆想都不要想直接打表出来,找有没有循环节,没有循环节再找规律。
  • 怎么找一个数的最大质因子呢?不需要素数筛,试除法就行。当然你开心的话,可以上Eratothenes筛或欧拉筛打素数表。试除法打素数表是会超时的。当然这里用到的素数还不大于100。
  • 时间复杂度:O(1)或O(\(M\sqrt{N}\))或O(\(M\log \log N\)),M=??

求最大质因子

  1. /**
  2. * find out the largest prime factor of given num.
  3. */
  4. int largestFactorOf(int num) {
  5. int iterateNumber = num;
  6. int q = (int) sqrt(num) + 1;
  7. int iterateFactor = 2;
  8. while (iterateFactor <= q) {
  9. while (iterateNumber % iterateFactor == 0)
  10. iterateNumber /= iterateFactor;
  11. if (iterateNumber == 1)
  12. break;
  13. ++iterateFactor;
  14. }
  15. return iterateNumber == 1 ? iterateFactor : iterateNumber;
  16. }

参考代码

  1. #include <stdio.h>
  2. /**
  3. * http://oeis.org/A195201
  4. * Description: for a(1) = 1, a(n) is the largest prime factor of
  5. * the number which is made up from a(n-1) and its last digit.
  6. * Input: T, and T lines of any n.
  7. * Output: a(n) for every n.
  8. * */
  9. const int fixedSection[4] = {
  10. 1, 11, 37, 29
  11. };
  12. const int cycleSection[26] = {
  13. 23, 233, 2333, 23333, 661, 601, 6011, 6679,
  14. 997, 907, 313, 241, 2411, 47, 53, 41, 137,
  15. 17, 59, 599, 857, 953, 9533, 13619, 19457, 821
  16. };
  17. int T, n;
  18. void read() {
  19. scanf("%d", &n);
  20. }
  21. void work(int caseCount) {
  22. printf("Case #%d: %d\n", caseCount, n <= 4 ? fixedSection[n - 1] : cycleSection[(n - 5) % 26]);
  23. }
  24. int main() {
  25. scanf("%d", &T);
  26. for (int i = 1; i <= T; ++i) {
  27. read();
  28. work(i);
  29. }
  30. return 0;
  31. }

### 1006. 飞来飞去的Zyj

Problem Description

Zyj在接电路板。他给出了要连接的线的列表,并按顺序进行连接。但是他不绕线,如果两个点连成的线段不与任何画线相交,就在上面把线画出来,否则就直接上飞线(即这根线不画在电路板上,而是在空中连接)。问,Zyj一共飞了几条线?

注意,如果其中一条线经过另一条线的端点,视为相交;但如果两条线在端点上相接,视为不相交。部分重叠视为相交,除非在端点上相接

Input

第一行T为数据组数(小于100)

每组数据的第一行n为连线数量(小于100),后面紧跟着n行(x1,y1)-(x2,y2)表示端点坐标。坐标值的绝对值小于100.

Output

对每组数据,输出Case #d: q,其中d为数据编号,q为飞线数量。

Sample Input

  1. 2
  2. 7
  3. (1,2)-(4,2)
  4. (4,2)-(4,1)
  5. (4,1)-(2,1)
  6. (2,1)-(2,4)
  7. (2,4)-(3,4)
  8. (3,4)-(3,3)
  9. (1,3)-(3,3)
  10. 4
  11. (1,1)-(7,1)
  12. (2,1)-(2,2)
  13. (3,1)-(4,1)
  14. (6,1)-(7,1)

Sample Output

  1. Case #1: 1
  2. Case #2: 2

样例解释:

  1. 4 ┎┐

  2. 3 ─╂┘

  3. 2 ─╂─┐

  4. 1 ┗─┘

  5. 1234



  6. 2

  7. 1 ─┸────────

  8.   ━━  ──(这行和上一行是叠在一起的)

  9. 1234567

  10. 其中粗线表示飞线

解题思路

  • 讲真,这题我也不会做。反正又是大佬的神作。
  • 后来搜了下,线段判交是有技巧的,常用的是向量积;也可以直接解析几何解方程。这里用了随便一搜能搜出来的快速排斥+跨立试验。
  • 需要注意的是1.按顺序连接;2.重叠相交;3.端点连接强制不相交;4.应当标记飞过的线。
  • 时间复杂度:O(\(N^2\))

参考代码(因为我不会做,直接用C++了)

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <algorithm>
  4. using std::max;
  5. using std::min;
  6. inline int feq(const double &a, const double &b) {
  7. return fabs(a - b) < 1e-7;
  8. }
  9. inline int fneq(const double &a, const double &b) {
  10. return !feq(a, b);
  11. }
  12. inline int fgeq(const double &a, const double &b) {
  13. return feq(a, b) || a > b;
  14. }
  15. struct Point {
  16. double x, y;
  17. Point() {}
  18. Point(double x, double y) : x(x), y(y) {}
  19. int operator!=(const Point &p) const {
  20. return fneq(x, p.x) || fneq(y, p.y);
  21. }
  22. };
  23. struct Line {
  24. double x, y;
  25. Line() {}
  26. Line(const Point &P0, const Point &P1) : x(P1.x - P0.x), y(P1.y - P0.y) {}
  27. double operator^(const Line &l) const {
  28. return x * l.y - y * l.x;
  29. }
  30. };
  31. typedef Point Segment[2];
  32. inline int quickReject(Segment &A, Segment &B) {
  33. return max(A[0].x, A[1].x) >= min(B[0].x, B[1].x) &&
  34. max(A[0].y, A[1].y) >= min(B[0].y, B[1].y) &&
  35. max(B[0].x, B[1].x) >= min(A[0].x, A[1].x) &&
  36. max(B[0].y, B[1].y) >= min(A[0].y, A[1].y);
  37. }
  38. inline int quickCross(Segment &A, Segment &B) {
  39. return (Line(B[0], A[0]) ^ Line(B[0], B[1])) * (Line(B[0], B[1]) ^ Line(B[0], A[1])) >= 0 &&
  40. (Line(A[0], B[0]) ^ Line(A[0], A[1])) * (Line(A[0], A[1]) ^ Line(A[0], B[1])) >= 0;
  41. }
  42. int flown[101];
  43. Segment seg[101];
  44. int main() {
  45. int T, N;
  46. scanf("%d", &T);
  47. for (int cse = 1; cse <= T; ++cse) {
  48. int res = 0;
  49. memset(flown, 0x0, sizeof(flown));
  50. scanf("%d%*c", &N);
  51. for (int i = 0; i < N; ++i) {
  52. scanf("%*c%lf%*c%lf%*c%*c%*c%lf%*c%lf%*c%*c", &seg[i][0].x, &seg[i][0].y, &seg[i][1].x, &seg[i][1].y);
  53. for (int j = 0; j < i; ++j)
  54. if (quickReject(seg[j], seg[i]) && quickCross(seg[j], seg[i]) &&
  55. seg[j][0] != seg[i][0] && seg[j][0] != seg[i][1] &&
  56. seg[j][1] != seg[i][0] && seg[j][1] != seg[i][1] && !flown[j]) {
  57. flown[i] = 1;
  58. ++res;
  59. break;
  60. }
  61. }
  62. printf("Case #%d: %d\n", cse, res);
  63. }
  64. return 0;
  65. }

### 1007. 灌水

Problem Description

CZJ和L2M在车上闲着没事拿了个水杯灌水,规定杯子的体积P和每次可以倒进杯子的水的最大体积Y,两个人轮流向杯子灌整数体积的水,最后灌满杯子的人获胜。

CZJ看起来很想赢的样子,L2M宽宏大量的给了先手。但是CZJ这么菜,你能教下CZJ起手要灌多少才能必胜吗?

Input

第一行是一个T,代表数据的组数T(T≤20000)。每组两个整数p,y(1≤p≤10000000,1≤y≤10000000)。

Output

对于每组数据输出CZJ起手灌水的数量,如果CZJ赢不了,打出GG。

Sample Input

  1. 2
  2. 3 1
  3. 2 1

Sample Output

  1. 1
  2. GG

解题思路

  • 首先这是一个入门级博弈问题。不知道博弈的也没关系,如果你有耐心,在纸上演算一下6以内的情况,就会发现规律。
  • 这个问题是巴什博奕先手赢。我们总考虑P>V,否则先手必胜的。
  • 我们先找一个先手必败态:P=Y+1。无论先手怎么倒水,总不能倒满,而后手总能把杯子倒满,所以这种情况先手必败。
  • 在其他情况下,能不能想办法把这个必败态转移给对手呢?考虑只有两局,第一局先手先倒M的水,后手和第二局先手总共倒N=P-M的水,并且要保证第二局先手胜。
  • 我们发现这相当于只有一局——不管先手倒多少体积为M的水,就当水杯体积是P-M好了,这样原本的后手等价成为了新的先手。如果剩下的体积刚好N=Y+1,那么原本的后手就陷入了先手必败困局
  • 如果除去一开始倒的M水,每一局都总共倒Y+1的水呢?因为Y<Y+1<2Y,新的先手永远最多只能倒1<=Z<=Y的水,而新的后手总能控制局面的变化,即他能倒Y+1-Z<=Y的水。控制每次两人总共倒Y+1的水,就赢了。
  • 为什么一定是Y+1,不能是+2、+3吗?因为再多的话,先手就不是必败了,先手总能取一个较小值使得后面剩下Y+1。
  • 所以一开始要抢先倒P%(Y+1)的水,后面总共倒的水体积是(Y+1)的倍数。

参考代码

  1. #include <stdio.h>
  2. int main() {
  3. int p, y;
  4. scanf("%d", &p);
  5. while (~scanf("%d%d", &p, &y))
  6. (p %= (y + 1)) ? printf("%d\n",p) : puts("GG");
  7. return 0;
  8. }

### 1008. 和

Problem Description

输入几个数字,求和。

Input

每行一个样例,由正整数组成,空格分开

Output

对每个样例,输出和。保证结果小于\(10^9\)

Sample Input

  1. 1 1
  2. 1 2

Sample Output

  1. 2
  2. 3

解题思路

  • 签到题,注意题面的“几个数字”
  • 用EOF判输入结束。可以用一个小char判断换行。

参考代码

  1. #include <stdio.h>
  2. int main() {
  3. int res, a, c;
  4. while (~scanf("%d", &res)) {
  5. while (scanf("%c", &c), c != '\n') {
  6. scanf("%d", &a);
  7. res += a;
  8. }
  9. printf("%d\n", res);
  10. }
  11. return 0;
  12. }

### 1009. 开饭

Problem Description

CZJ的舍友请CZJ吃饭,作为舍友CZJ是不会放过吃穷舍友的机会,但自己身体又不好,不能吃太多也不能吃太少。假设每样食物都有一个饱腹度和价值,请你帮他算下刚好吃饱的情况下最多可以吃他舍友多少钱。

Input

第一行是一个T,代表数据的组数T(T≤100001)。每组两个整数p,y(1≤p≤1000,1≤y≤100),分别表示食物的数量和CZJ的饱腹度。

然后p个数\(Q_{p_i}\)表示第\(p_i\)个食物的价值,之后p个数\(M_{p_i}\)表示吃第\(p_i\)个食物得到的饱腹度。(1≤\(Q_{p_i}\)≤10000,1≤\(M_{p_i}\)≤1000)。

Output

对于每一个样例输出CZJ能吃下的最大价值。

Sample Input

  1. 1
  2. 5 100
  3. 11 6 7 5 6
  4. 40 50 60 20 30

Sample Output

  1. 18

解题思路

  • 0-1背包问题求最大值。入门级dp。
  • 对于这题有很多人过没有意外。只要你搜一下什么叫“动态规划”或者“dp”,把0-1背包的递推往这一套,就A了。
  • 推荐学习资料:“背包九讲”、“动态规划32讲”。

参考代码

  1. #include <stdio.h>
  2. #include <string.h>
  3. #define MAXN 1010
  4. int C[MAXN], W[MAXN], dp[MAXN];
  5. void init() {
  6. memset(C, 0x0, sizeof(C));
  7. memset(W, 0x0, sizeof(W));
  8. // dp数组初始值要足够小。这里一个int是0x80808080
  9. memset(dp, 0x80, sizeof(dp));
  10. dp[0] = 0;
  11. }
  12. void getMax(int &a, int b) {
  13. if (a < b) a = b;
  14. }
  15. int N, V;
  16. void read() {
  17. scanf("%d%d", &N, &V);
  18. for (int i = 0; i < N; ++i) scanf("%d", W + i);
  19. for (int i = 0; i < N; ++i) scanf("%d", C + i);
  20. }
  21. void work() {
  22. for (int i = 0; i < N; ++i)
  23. for (int v = V; v >= C[i]; --v)
  24. getMax(dp[v], dp[v - C[i]] + W[i]);
  25. printf("%d\n", dp[V]);
  26. }
  27. int main() {
  28. int T;
  29. scanf("%d", &T);
  30. while (T--) {
  31. init();
  32. read();
  33. work();
  34. }
  35. return 0;
  36. }

### 1010. Zyj消极比赛

Problem Description

ACM比赛剩下最后10分钟,其他人都已经收拾好东西准备走人,Zyj才从睡梦中醒来。Zyj可以看到所有其他人的过题情况,他希望得到的名次在a到b之间,问有几种可选择的方案?(假设其他人的提交时间即使加上罚时也早于Zyj的提交,毕竟剩下10分钟了都)

Input

第一行为T(0<T<100),代表有T组数据。

每组数据中第一行为两个数字m n a b,由空格隔开,代表这次比赛有m道题(由于字母数量的限制,0<m<27),n个其他人,Zyj希望得到的名次x满足a<x<b (-1<n,a,b<1000)

下面n行为每个人每道题的通过情况,1为已通过,0为未通过

Output

对每个样例,输出Case #k: ans,其中k为样例编号,ans为方案数量。

Sample Input

  1. 2
  2. 26 1 0 2
  3. 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
  4. 26 1 0 2
  5. 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0

(Case #1是25个1 1个0,#2是1个0 24个1 1个0)

Sample Output

  1. Case #1: 1
  2. Case #2: 27

Hint

样例解释:

Case #1中Zyj必须AK(全部题都做出来)才可以得到大于0小于2的名次,即第1名。不要怕他做不完,那可是Zyj

Case #2中Zyj可以AK,也可以任意选一道不做

ACM排名规则见http://scnuacm2015.sinaapp.com/php/presentation.php

解题思路

  • 对于这种题我只能无语。
  • 这里有个隐含条件,因为Zyj是最后做题,罚时爆炸,所以无论他做多少题,都是同题数的最后一名
  • 然后累加出每个人过题的数量,进而统计过0≤x≤m题的人数,从最高名次到最低名根据Zyj想要的名次去求组合数就好。
  • 一些错误点:
    1. 没有处理好边界,对于不可能存在/达到的排名,计算结果不为0;
    2. 同题数计算排名时没有考虑zyj是最后做题;
    3. 没有加上当有人过0题时,zyj拿最后一名也可以过0题的1种情况;
    4. 并列排名不顺延,比如1题的最后一名可能是x,那么0题的并列排名是x+1而不是n;
    5. 有没看懂题的,把过题总数直接作为排名(也是没考虑过题时间会影响排名);
    6. 还有一些奇怪的错误..不会算组合数or阶乘..有的没考虑除0..有的没考虑计算过程不整除..有的..我也不知道。
  • 时间复杂度:\(O(m*m)\)或\(O(m+m*m)\)(公式法预处理)或\(O(m+m)\)(预处理阶乘及其逆元)

参考代码1

  1. #include <stdio.h>
  2. #include <string.h>
  3. /*
  4. * count[k] 记录了过了k题的人数
  5. */
  6. int m, n, a, b;
  7. int count[27];
  8. void init() {
  9. memset(count, 0x0, sizeof(count));
  10. }
  11. void read() {
  12. scanf("%d%d%d%d", &m, &n, &a, &b);
  13. for (int i = 0; i < n; ++i) {
  14. int acc = 0, temp;
  15. for (int j = 0; j < m; ++j) {
  16. scanf("%d", &temp);
  17. acc += temp;
  18. }
  19. // 实际上count[0]没有利用价值
  20. ++count[acc];
  21. }
  22. }
  23. // 求组合数 - 魔幻除法(可以想想为什么能整除)
  24. int C(int n, int x) {
  25. int res = 1;
  26. if (n - x > x) x = n - x;
  27. n -= x;
  28. for (int i = 1; i <= n; i++)
  29. res = res * (x + i) / i;
  30. return res;
  31. }
  32. int work() {
  33. int res = 0, rank = 1;
  34. for (int i = m; i > 0; --i) {
  35. // zyj 做i题能拿的名次
  36. rank += count[i];
  37. if (a < rank && rank < b)
  38. res += C(m, i);
  39. }
  40. // 就算做0题,zyj也是拿同做1题一样的排名
  41. if (a < rank && rank < b)
  42. res += 1;
  43. return res;
  44. }
  45. int main() {
  46. int T;
  47. scanf("%d", &T);
  48. for (int cse = 1; cse <= T; ++cse) {
  49. init();
  50. read();
  51. printf("Case #%d: %d\n", cse, work());
  52. }
  53. return 0;
  54. }

参考代码2

  1. #include <stdio.h>
  2. #include <string.h>
  3. /*
  4. * count[k] 记录了过了k题的人数
  5. */
  6. int m, n, a, b;
  7. int count[27];
  8. void init() {
  9. memset(count, 0x0, sizeof(count));
  10. }
  11. void read() {
  12. scanf("%d%d%d%d", &m, &n, &a, &b);
  13. for (int i = 0; i < n; ++i) {
  14. int acc = 0, temp;
  15. for (int j = 0; j < m; ++j) {
  16. scanf("%d", &temp);
  17. acc += temp;
  18. }
  19. // 实际上count[0]没有利用价值
  20. ++count[acc];
  21. }
  22. }
  23. // 求组合数 - 公式预处理法
  24. int C[27][27];
  25. const int MOD = 1000000007;
  26. void pre() {
  27. C[0][0] = C[1][0] = C[1][1] = 1;
  28. // 公式 C(n,x) = C(n-1,x-1) + C(n-1,x)
  29. for(int n = 2; n <= 26; n++) {
  30. C[n][0] = C[n][n] = 1;
  31. for(int x = 1; x < n; x++)
  32. C[n][x] = (C[n-1][x-1] + C[n-1][x]) % MOD;
  33. }
  34. }
  35. int work() {
  36. int res = 0, rank = 1;
  37. for (int i = m; i > 0; --i) {
  38. // zyj 做i题能拿的名次
  39. rank += count[i];
  40. if (a < rank && rank < b)
  41. res += C[m][i];
  42. }
  43. // 就算做0题,zyj也是拿同做1题一样的排名
  44. if (a < rank && rank < b)
  45. res += 1;
  46. return res;
  47. }
  48. int main() {
  49. pre();
  50. int T;
  51. scanf("%d", &T);
  52. for (int cse = 1; cse <= T; ++cse) {
  53. init();
  54. read();
  55. printf("Case #%d: %d\n", cse, work());
  56. }
  57. return 0;
  58. }

### 1011. Oyk剪纸

Problem Description

Oyk要把一张矩形纸剪成N张一样的矩形不留剩余,使用的方法为

1.从一个方向剪若干(可能为0)刀

2.从与其垂直的方向再剪若干刀

那么他至少要剪几刀?

Input

多(少于1000)组样例,每行一个数字N(大于0小于1亿)。

Output

对每个样例,输出一行结果。

Sample Input

  1. 4
  2. 5
  3. 60

Sample Output

  1. 2
  2. 4
  3. 14

Hint

解释:

  1. ┌┬┐┌┬┬┬┬┐

  2. ├┼┤└┴┴┴┴┘

  3. └┴┘

解题思路

  • 首先剪的方向只能是与纸边平行或垂直。斜着剪会产生剩余,而且也产生负收益(浪费了剪的次数)。
  • 于是问题分解成了\(N=x * \frac{N}{x}\),\(r=(x-1)+(\frac{N}{x}-1)\),要让\(r\)尽可能小。不记得什么不等式了。
  • 直接任性求导,\(r'=1-\frac{N}{x^2}=0\),在\(x=\sqrt{N}\)处\(r\)取得最小值。但注意x要是整数(不可能剪半刀)。
  • 时间复杂度:O(\(\sqrt{N}\)),最坏情况由N是素数产生

参考代码

  1. #include <stdio.h>
  2. #include <math.h>
  3. int main() {
  4. int N, q;
  5. while (~scanf("%d", &N)) {
  6. q = (int) sqrt(N);
  7. for (; q > 1; --q)
  8. if (N % q == 0) break;
  9. printf("%d\n", q + N / q - 2);
  10. }
  11. return 0;
  12. }

出题及解题总结

我出的两题

  • 1004、1005都是我出的题。设计是一简单一难的。
  • 1004极角排序我想对新生来说还是挺难想到并做出的,事实上我出数据都快出成傻逼了;
  • 1005循环节打表可能我太高估新生水平了(?),因为我并没有出1e9的数据去卡素数表,而且数据范围设置非常小,试除法也不至于TLE,基本上是随意过的。理想情况是再不济一堆WA或TLE吧,结果伤心的发现没人做(逃)。

整体情况总结

  1. 首先看统计数据,1001的WA接近500,这里Czj应该背锅,题面数据范围与实际不符。还有一个不输出回车的坑爹设定,强行卡题意,WA得惨不忍睹,严重打击了部分同学信心。
  2. 统计数据还有第二个明显峰度在1003的OLE上。因为我看不到提交代码,没法分析。
    • 有可能是hdoj的OLE是这么判的:输出超过标准答案即判OLE。如果是这样,很多OLE应当划到WA里面。
    • 但是也有反映将C++的输出换成C语言的输出即可通过。这应当是hdoj的锅了。
  3. 还有两个WA小高峰位于1003、1008。
    • 1003的确是很多同学根本没接触到位运算的知识。
    • 而1008也是强行卡题意,给的样例不规范,还不如只给一个。另外也有很多同学并不知道EOF判输入结束。
  4. 从所有题目通过率来看,
    • 1001、1003、1005、1007、1008、1011是正常水平在七天时间内能做出的。
    • 1009尽管是入门级dp,但也涉及了算法,虚高是因为生源有部分接触过OI,以及的确简单,能间接搜到答案。
  5. 从难度上看,
    • 1002爆冷是正常,原因大家都懂。
    • 1004的确算是较难的了,之后改题面应该称得上全场最难。
    • 1006爆冷正常,因为我也不会做(逃),好吧是因为涉及了计算几何,不应该出现在新生赛中。
    • 1010爆冷是意料之外,也是情理之中,这种卡题意挖坑的题必须找出题人背锅。
  6. 1001、1008是两个签到题。预计决赛名额定在解出两题以上。
  7. 我校出题质量一年比一年差,迟早要完。

历届新生赛&别人的新生赛

  1. 华南师大2014新生赛:http://3.scnuacm2015.sinaapp.com/?p=38
  2. 广工2016新生决赛网络同步赛重现:http://gdutcode.sinaapp.com/contest.php?cid=1051
  3. ....

建议、意见、吐槽

欢迎在下方评论区提出问题,12月内我都会回复and更新。

本文基于知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议发布,欢迎引用、转载或演绎,但是必须保留本文的署名BlackStorm以及本文链接http://www.cnblogs.com/BlackStorm/p/SCNUCPC_2016_For_Freshman_Preliminary_Solution.html,且未经许可不能用于商业目的。如有疑问或授权协商请与我联系

SCNU ACM 2016新生赛初赛 解题报告的更多相关文章

  1. SCNU ACM 2016新生赛决赛 解题报告

    新生初赛题目.解题思路.参考代码一览 A. 拒绝虐狗 Problem Description CZJ 去排队打饭的时候看到前面有几对情侣秀恩爱,作为单身狗的 CZJ 表示很难受. 现在给出一个字符串代 ...

  2. 北邮新生排位赛1解题报告d-e

    话说cdsn要是前面插入源代码又什么都不放就会出现奇怪的源代码?不知道是哪个网页的 407. BLOCKS 时间限制 1000 ms 内存限制 65536 KB 题目描述 给定一个N∗M的矩阵,求问里 ...

  3. nowcoder(牛客网)OI测试赛2 解题报告

    qwq听说是一场普及组难度的比赛,所以我就兴高采烈地过来了qwq 然后发现题目确实不难qwq.....但是因为蒟蒻我太蒻了,考的还是很差啦qwq orz那些AK的dalao们qwq 赛后闲来无事,弄一 ...

  4. NOIP2018初赛 解题报告

    前言 \(NOIP2018\)初赛已经结束了,接下来就要准备复赛了. 不过,在此之前,还是先为初赛写一篇解题报告吧. 单项选择题 送分题.(虽然我还是做错了)可以考虑将它们全部转化为\(10\)进制, ...

  5. 20161005 NOIP 模拟赛 T2 解题报告

    beautiful 2.1 题目描述 一个长度为 n 的序列,对于每个位置 i 的数 ai 都有一个优美值,其定义是:找到序列中最 长的一段 [l, r],满足 l ≤ i ≤ r,且 [l, r] ...

  6. 冲刺Noip2017模拟赛5 解题报告——五十岚芒果酱

    1. 公约数(gcd) [问题描述] 给定一个正整数,在[,n]的范围内,求出有多少个无序数对(a,b)满足 gcd(a,b)=a xor b. [输入格式] 输入共一行,一个正整数n. [输出格式] ...

  7. 冲刺Noip2017模拟赛3 解题报告——五十岚芒果酱

    题1  素数 [问题描述] 给定一个正整数N,询问1到N中有多少个素数. [输入格式]primenum.in 一个正整数N. [输出格式]primenum.out 一个数Ans,表示1到N中有多少个素 ...

  8. 冲刺Noip2017模拟赛2 解题报告——五十岚芒果酱

    题1 牛跑步(running) [题目描述] 新牛到部队,CG 要求它们每天早上搞晨跑,从 A 农场跑到 B 农场.从 A 农场到 B 农场中有 n- 个路口,分别标上号,A 农场为 号,B 农场为 ...

  9. 冲刺Noip2017模拟赛1 解题报告——五十岚芒果酱

    题1 国际象棋(chess) [问题描述] 有N个人要参加国际象棋比赛,该比赛要进行K场对弈.每个人最多参加2场对弈,最少参加0场对弈.每个人都有一个与其他人都不相同的等级(用一个正整数来表示).在对 ...

随机推荐

  1. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(16)-权限管理系统-漂亮的验证码

    系列目录 我们上一节建了数据库的表,但我发现很多东西还未完善起来,比如验证码,我们先做好验证码吧,验证码我们再熟悉不过了,为了防止恶意的登录,我们必须在登录页面加入验证码,下面我将分享一个验证码,这个 ...

  2. 2. Struts2 基础

    1. Struts2简介 Struts2是一个WEB端MVC框架.作为比较早的MVC 框架之一,Struts2在使用中还是比较多的.虽然个人感受没有SpringMVC还那么的好用 Struts2 官网 ...

  3. 门面模式的典型应用 Socket 和 Http(post,get)、TCP/IP 协议的关系总结

    门面模式的一个典型应用:Socket 套接字(Socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息: 连接使用的 ...

  4. Linux驱动开发—— of_property_read_u8

    在使用設備樹的時候, 有時會遇到下面的問題. 在設備樹中配置的屬性如下: fusb301,init-mode = <0x20>; 但是在驅動中讀出的卻是: rc = of_property ...

  5. xss和csrf攻击

    xss(cross site scripting)是一种最常用的网站攻击方式. 一.Html的实体编码 举个栗子:用户在评论区输入评论信息,然后再评论区显示.大概是这个样子: <span> ...

  6. 1.JAVA之GUI编程概述

          下列内容为本人看毕向东老师java视频教程学习笔记! JAVA GUI图形用户界面编程: Windows 操作系统提供两种操作方式:                             ...

  7. 使用localResizeIMG3+WebAPI实现手机端图片上传

    前言 惯例~惯例~昨天发表的使用OWIN作为WebAPI的宿主..嗯..有很多人问..是不是缺少了什么 - - 好吧,如果你要把OWIN寄宿在其他的地方...代码如下: namespace Conso ...

  8. CSS知识总结(六)

    CSS常用样式 4.段落样式 1)行高 控制段落内每行高度 line-height : normal | length 例子 源代码: /* CSS代码 */ .normal{ line-height ...

  9. 使用 Windows Phone Toolkit 的 Tilt 效果

    上一篇文章分享了如何使控件具有摁下的效果(在WindowsPhone中使控件具有Tilt效果),实现方式是在项目中添加新的类文件,其实,如果项目引用了Windows Phone Toolkit,那么就 ...

  10. 使用图片视频展示插件blueimp Gallery改造网站的视频图片展示

    在很多情况下,我们网站可能会展示我们的产品图片.以及教程视频等内容,结合一个比较好的图片.视频展示插件,能够使得我们的站点更加方便使用,也更加酷炫,在Github上有很多相关的处理插件可以找来使用,有 ...