5/11
2016 Multi-University Training Contest 3
官方题解
暴力 A Sqrt Bo(CYD)
题意:
问进行多少次开根号向下取整能使给定的值为1,若为5次以上,则输出TAT;
思路:
有5次这个限制,得知值最大为10的11次方左右,存下后运算即可,注意初始值为0时运算的次数是大于5次的,输出TAT;
代码:
#include <bits/stdc++.h> using namespace std; char str[105]; int main() { int i,j,k; while(scanf("%s",str)!=EOF) { double ans=0; int l=strlen(str); if(l>12 || (l==1 && str[0]=='0')) { printf("TAT "); continue; } for(i=0;i<l;i++) { ans=ans*10; ans+=str[i]-'0'; } int cnt=0; while(ans-1.0>=1e-6) { ans=floor(sqrt(ans)); cnt++; } if(cnt>5) printf("TAT "); else printf("%d ",cnt); } return 0; }
线性期望 B Permutation Bo(BH)
题意:
有n个数字,范围1~n,对于一个排列,有,已知,后面意思是判断大于左右两边的数字,结果1或0,然后与相乘累加,求对于全排列,f(h)的期望值。
思路:
对于这种线性期望题,对于每一个,求它能贡献的概率*权值,权值已知,也就是要求在全排列出现的概率。易得概率为,,答案为。
代码:
#include <bits/stdc++.h> const int N = 1e3 + 5; int c[N]; int main() { int n; while (scanf ("%d", &n) == 1) { int s1 = 0, s2 = 0; for (int i=1; i<=n; ++i) { scanf ("%d", c+i); if (i == 1 || i == n) s1 += c[i]; else s2 += c[i]; } double ans = 0; if (n == 1) { ans = c[1]; } else { ans = (double) s1 / 2 + (double) s2 / 3; } printf ("%.6f ", ans); } return 0; }
博弈/打表 Life Winner Bo(BH)
题意:
n*m的棋盘,在(1,1)的位置放一个国际象棋中的棋子(国王,车,骑士,皇后),国王相当于中国象棋的士的走法,车就是车,骑士相当于马(没有马脚),皇后有士的方向,车的距离。每次只能往右下走,B先走,问谁必胜。
思路:
反思:一开始我就想用从下往上递推状态图,写了四个函数分别是四个棋子的走法,对于车和皇后的后继状态比赛时先用了树状数组T了,后来用了数组求前缀后省了logn,但还是T了,一脸懵逼。后来才发现T<=1000,再加上我每次都的跑,不T才怪。后来脑子清醒点再加上队友的指点,才想到还有预处理这条路,存下所有4*n*m的结果,然后输出答案,结果还是以WA终场。赛后知道皇后是经典的威佐夫博弈,完全没听说过,博弈只会简单的递推。。。,
看完题解知道车原来就是经典的Nim,至于另外两个可以打表找规律也可以直接递推的。经检验,比赛的递推代码里只有皇后是有问题的,补题时打表找出马和国王的规律。代码里还留有递推的正确代码。
另外,只有马有平局的情况,出现这种可能的局面是B在没有必胜的可能情况下,平局比必败优,选择走平局的情况(往不能走的边界方向走)。
代码:
#include <bits/stdc++.h> const int N = 1e3 + 5; const int W = 1e3; const double a = (sqrt (5.0) + 1) / 2; int win[N][N]; int dp[4][N][N]; //1 2 3 int row[N][N], col[N][N]; int tp, n, m; void debug() { for (int i=1; i<=20; ++i) { for (int j=1; j<=20; ++j) { printf ("%c ", dp[3][i][j]); } puts (""); } } char solve_rook() { return (n ^ m) ? 'B' : 'G'; } char solve_queen() { if (n > m) std::swap (n, m); n--; m--; int k = m - n; if (n == (int) (a * k)) return 'G'; else return 'B'; } char solve_king() { if (n & 1 && m & 1) return 'G'; else return 'B'; } char solve_knight() { if (n == m) { if (n % 3 == 1 && n >= 4) return 'G'; else return 'D'; } else { if (n > m) std::swap (n, m); if (n + 1 == m && n % 3 == 2 && n >= 2) return 'B'; else return 'D'; } } void init() { n = m = W; //solve_king (); //solve_knight (); //solve_rook (); //solve_queen (); //WA TAT } int main() { //init (); int T; scanf ("%d", &T); while (T--) { scanf ("%d%d%d", &tp, &n, &m); if (tp == 1) { printf ("%c ", solve_king ()); } else if (tp == 2) { printf ("%c ", solve_rook ()); } else if (tp == 3) { printf ("%c ", solve_knight ()); //printf ("%c ", dp[tp][n][m]); } else { printf ("%c ", solve_queen ()); } } return 0; } /* void solve_rook() { memset (win, 0, sizeof (win)); win[n][m] = 0; for (int i=n; i>=1; --i) { for (int j=m; j>=1; --j) { if (i == n && j == m) continue; win[i][j] = 0; if (j < m) { row[i][j] = row[i][j+1]; if (row[i][j+1] != m - j) win[i][j] = 1; } if (i < n) { col[j][i] = col[j][i+1]; if (col[j][i+1] != n - i) win[i][j] = 1; } if (win[i][j]) row[i][j]++, col[j][i]++; int nn = W - i + 1, mm = W - j + 1; dp[2][nn][mm] = win[i][j] ? 'B' : 'G'; } } } */ /* void solve_king() { for (int i=n; i>=1; --i) { for (int j=m; j>=1; --j) { if (i == n && j == m) continue; if (j < m) { if (!win[i][j+1]) win[i][j] = 1; if (i < n && !win[i+1][j+1]) win[i][j] = 1; } if (i < n && !win[i+1][j]) win[i][j] = 1; int nn = W - i + 1, mm = W - j + 1; dp[1][nn][mm] = win[i][j] ? 'B' : 'G'; } } debug (); }*/ /* void solve_knight() { memset (win, 0, sizeof (win)); for (int i=1; i<=m; ++i) win[n][i] = -1; for (int i=1; i<=n; ++i) win[i][m] = -1; win[W-1][W-1] = -1; win[n][m] = 0; for (int i=n; i>=1; --i) { for (int j=m; j>=1; --j) { if (i == n && j == m) continue; if (win[i][j] == -1) continue; if (j < m && i < n-1 && !win[i+2][j+1]) win[i][j] = 1; if (j < m-1 && i < n && !win[i+1][j+2]) win[i][j] = 1; if (!win[i][j]) { if (j < m && i < n-1 && win[i+2][j+1] == -1) win[i][j] = -1; if (j < m-1 && i < n && win[i+1][j+2] == -1) win[i][j] = -1; } int nn = W - i + 1, mm = W - j + 1; dp[3][nn][mm] = win[i][j] == 1 ? 'B' : win[i][j] == 0 ? 'G' : 'D'; } } debug (); } */
物理 J Rower Bo(CYD)
题意:
给定x轴方向的水流速度、一艘船的初速度和在y轴上的起始位置,求船开到原点处的时间,船的方向总是朝向原点;
思路:
在x轴方向和朝向原点方向列出方程:
;
.
积分后得:
;
.
将上式积分部分带入下式可化解得:,这样就巧妙消去了积分环节,避免了求与t的关系,若按x,y轴方向分解速度,就要求这个积分,比较伤。
最后特判一下a=0和v1<=v2的情况。
代码:
#include <bits/stdc++.h> using namespace std; int main() { int a,v1,v2; while(scanf("%d %d %d",&a,&v1,&v2)!=EOF) { if(a==0) { printf("0.0000000000 "); continue; } if(v1<=v2) { printf("Infinity "); continue; } double A=a,V1=v1,V2=v2; printf("%.10f ",V1*A/(V1*V1-V2*V2)); } return 0; }
鸽巢原理 K Teacher Bo(BH)
题意:
平面上n个点,要求判断是否存在两对点对(A,B)和(C,D),使得点对的曼哈顿距离相同,点对可存在一个公共点。
思路:
看到题目上xi,yi<=M<=100000,有n^2的点对,根据鸽巢原理,如果存在,最多只要判断M次就可以,时间复杂度
代码:
#include <bits/stdc++.h> const int N = 1e5 + 5; std::map<int, int> vis; struct Point { int x, y; }p[N]; int dis(Point &a, Point &b) { return abs (a.x - b.x) + abs (a.y - b.y); } int main() { int T; scanf ("%d", &T); while (T--) { int n, m; scanf ("%d%d", &n, &m); bool flag = false; vis.clear (); for (int i=1; i<=n; ++i) { scanf ("%d%d", &p[i].x, &p[i].y); if (!flag) { for (int j=1; j<i; ++j) { int d = dis (p[j], p[i]); if (vis.count (d) == 0) { vis[d] = 1; } else { flag = true; break; } } } } puts (flag ? "YES" : "NO"); } return 0; }