HDOJ 4945 2048
见:http://www.cnblogs.com/james47/p/3914893.html
HDOJ 4946 Area of Mushroom
题意:一个无限大的二维平面,有很多点,有坐标有速度,如果对于某个位置,有个点能比任何点严格地先到达,那么这个点属于它。问对于每个点是否能管辖无限大的面积。
分析:显然速度小的不用考虑。只需要考虑速度最大的那些点。然后最大速度为0也不用考虑。然后想想就发现,速度最大的点只有在它们组成的凸包边界上,才能管辖无限面积。要么是组成凸包的顶点,要么是凸包的边上的点(至少能管辖一条无限长射线)。然后就是求凸包了。还有一点就是如果两个速度最大的点重合,它们都不能算答案,但是还得把它们加入点集求凸包。不会计算几何。。然后不知道怎么把凸包的边上的点也包含到凸包里,于是就再后来又做一个o(n^2)的暴力判断。学长说只要把下面代码里的<=改成<就可以了,然后还要把重点去掉一个(排序完o(n)筛除),因为重点同时在栈内,会组成一个零向量,下一点不管是往什么方向偏转都会被加入栈,就跪了。
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #include<vector> 6 using namespace std; 7 8 const double eps = 1e-8; 9 inline int sign(double a){ 10 return a < -eps ? -1 : a > eps; 11 } 12 #define cross(p1, p2, p3) ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) 13 #define crossOp(p1, p2, p3) sign(cross(p1, p2, p3)) 14 struct stu{ 15 int x, y, v, id; 16 } s[1000]; 17 struct point{ 18 int x, y, id; 19 point(){} 20 point(int _x, int _y): x(_x), y(_y){} 21 bool operator < (const point &p) const{ 22 int c = sign(x - p.x); 23 if (c) return c == -1; 24 return sign(y - p.y) == -1; 25 } 26 point operator -(const point &p) const{ 27 return point(x - p.x, y - p.y); 28 } 29 double dot(const point &p) const{ 30 return x * p.x + y * p.y; 31 } 32 }; 33 int n, cas = 0; 34 bool uni[1000]; 35 char ans[1000]; 36 37 int onSegment(point p, point q1, point q2) 38 { 39 return crossOp(q1, q2, p) == 0 && sign((p-q1).dot(p-q2)) <= 0; 40 } 41 vector<point> p, ans1; 42 vector<point> convexHull(vector<point> ps) 43 { 44 int n = ps.size(); 45 if (n <= 1) 46 return ps; 47 sort(ps.begin(), ps.end()); 48 vector<point> qs; 49 for (int i = 0; i < n; qs.push_back(ps[i++])){ 50 while(qs.size() > 1 && crossOp(qs[qs.size()-2], qs.back(), ps[i]) <= 0) 51 qs.pop_back(); 52 } 53 for (int i = n - 2, t = qs.size(); i >= 0; qs.push_back(ps[i--])){ 54 while((int)qs.size() > t && crossOp(qs[(int)qs.size()-2], qs.back(), ps[i]) <= 0) 55 qs.pop_back(); 56 } 57 qs.pop_back(); 58 return qs; 59 } 60 61 void output() 62 { 63 printf("Case #%d: %s\n", ++cas, ans); 64 return ; 65 } 66 int main() 67 { 68 while(scanf("%d", &n) && n) 69 { 70 for (int i = 0; i < n; i ++) ans[i] = '0'; 71 ans[n] = '\0'; 72 int maxv = -1; 73 for (int i = 0; i < n; i++){ 74 scanf("%d %d %d", &s[i].x, &s[i].y, &s[i].v); 75 s[i].id = i; 76 maxv = max(maxv, s[i].v); 77 } 78 if (maxv == 0){ 79 output(); 80 continue; 81 } 82 memset(uni, true, sizeof(uni)); 83 for (int i = 0; i < n; i++){ 84 if (uni[i]) 85 for (int j = i+1; j < n; j++){ 86 if (s[i].x == s[j].x && s[i].y == s[j].y && s[i].v == s[j].v) 87 uni[i] = uni[j] = false; 88 } 89 } 90 // for (int i = 0; i < n; i++) 91 // if (uni[i]) printf("%d\n", i); 92 p.clear(); 93 for (int i = 0; i < n; i++){ 94 if (s[i].v == maxv){ 95 point tmp; 96 tmp.x = s[i].x; 97 tmp.y = s[i].y; 98 tmp.id = s[i].id; 99 p.push_back(tmp); 100 } 101 } 102 // for (int i = 0; i < p.size(); i++) 103 // printf("%d %d %d\n", p[i].x, p[i].y, p[i].id); 104 ans1 = convexHull(p); 105 // for (int i = 0; i < ans1.size(); i++) 106 // printf("%d %d %d\n", ans1[i].x, ans1[i].y, ans1[i].id); 107 for (int i = 0; i < ans1.size(); i++) 108 if (uni[ans1[i].id]) ans[ans1[i].id] = '1'; 109 for (int i = 0; i < n; i++) 110 if (ans[i] == '0' && s[i].v == maxv && uni[i]){ 111 point now; 112 now.x = s[i].x; 113 now.y = s[i].y; 114 now.id = i; 115 for (int j = 0; j < ans1.size()-1; j++) 116 if (onSegment(now, ans1[j], ans1[j+1])){ 117 ans[i] = '1'; 118 break; 119 } 120 if (onSegment(now, ans1[0], ans1[ans1.size()-1])){ 121 ans[i] = '1'; 122 } 123 } 124 output(); 125 } 126 return 0; 127 }
HDOJ 4950 Monster
题意:每回合可以打怪兽,怪兽每轮回血。每轮可以选择休息或打,连续最多打k轮。问能否打死。
分析:作为一只勤劳的小蜜蜂,我们肯定要一直打k轮。那么就判断三种情况能不能打死就可以了。一个是一下就打死了,一个是k轮打死了,一个是磨血磨死的。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 5 long long h, a, b, k; 6 int main() 7 { 8 int cas = 0; 9 while(scanf("%lld %lld %lld %lld", &h, &a, &b, &k)) 10 { 11 if (h+a+b+k <= 0) break; 12 printf("Case #%d: ", ++cas); 13 bool yes = false; 14 if (h <= a) yes = true; 15 if (k*a - (k-1)*b >= h) yes = true; 16 if (k*a - (k+1)*b > 0) yes = true; 17 if (yes) puts("YES"); 18 else puts("NO"); 19 } 20 return 0; 21 }
HDOJ 4951 Multiplication table
题意:题面很复杂。就是给了一个p进制的一位乘法表,然后0...p-1重新做了个映射,给你新的乘法表,问你每个数在这个乘法表里是哪个数。
分析:第一眼没看懂,第二眼没看懂,第三眼猜懂了,感觉是很厉害的题。然后我们就发现0和1其实很好求,某一行如果全部相同,某一列十位和个位全部相同,那么他们对应的就是0,即0*0 = 00。然后1也是类似。题解给了一个很神奇的性质,并不想证明。。(目测也不好证),即特判完0和1,然后剩下的数,统计每行十位的不同数数目,那么这一行就是这个数目映射得到的。比如样例第0行十位的数有1,2,3三种,那么说明3映射成了0。(好吧我自己也没看懂自己写的)
然后出题人表示做法很多,比如你有了0和1,找p-1是哪个是很容易的,然后(p-1)*(p-1) = p(p-2) + 1,这样就找到p-2了,然后(p-1)(p-k) = p(p-k-1) + k,可以陆续找p-3,p-4。。就是一个递归的过程。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 int p, cas = 0; 7 int a[510][510], b[510][510]; 8 int ans[510]; 9 bool v[510]; 10 int read() 11 { 12 int ret = 0; 13 char ch = getchar(); 14 while(ch < '0' || ch > '9') ch = getchar(); 15 while(ch >= '0' && ch <= '9'){ 16 ret = ret * 10 + ch - '0'; 17 ch = getchar(); 18 } 19 return ret; 20 } 21 // 22 //int read() 23 //{ 24 // int ret = 0; 25 // bool flag = 0; 26 // char ch; 27 // while(1){ 28 // ch = getchar(); 29 // if (ch >= '0' && ch <= '9') {flag = 1; ret = ret*10 + ch-'0';} 30 // else if (flag) return ret; 31 // } 32 //} 33 int find0() 34 { 35 for (int i = 0; i < p; i++){ 36 bool flag = true; 37 int x = a[i][0]; 38 for (int j = 0; j < p; j++) 39 if (x != a[i][j] || x != b[i][j] || x != a[j][i] || x != b[j][i]) {flag = 0; break;} 40 if (flag) return i; 41 } 42 } 43 int find1() 44 { 45 for (int i = 0; i < p; i++){ 46 if (a[i][i] == ans[0] && b[i][i] == i) return i; 47 } 48 } 49 int main() 50 { 51 while(scanf("%d", &p)) 52 { 53 if (p == 0) break; 54 for (int i = 0; i < p; i++) 55 for (int j = 0; j < p; j++){ 56 //scanf("%d %d", &a[i][j], &b[i][j]); 57 a[i][j] = read();b[i][j] = read(); 58 } 59 ans[0] = find0(); 60 ans[1] = find1(); 61 for (int i = 0; i < p; i++) 62 if (i != ans[0] && i != ans[1]){ 63 memset(v, 0, sizeof(v)); 64 for (int j = 0; j < p; j++) v[a[i][j]] = true; 65 int cnt = 0; 66 for (int j = 0; j < p; j++) if (v[j]) cnt++; 67 ans[cnt] = i; 68 } 69 printf("Case #%d: ", ++cas); 70 for (int i = 0; i < p; i++) 71 printf("%d%c", ans[i], i==p-1?'\n':' '); 72 } 73 return 0; 74 }
HDOJ 4952 Number Transformation
题意:给一个x和k,k是k次操作,第i次操作要求当前x是i的倍数,否则要变成一个比x大而且是i倍数的最小的数,问k次操作后x是多少。
分析:开始还觉得要求1..k的lcm,后来发现根本不对。。后来反正队友打了个表,发现暴力做到sqrt(x)就可以了,我也没听明白证明。。题解的思路倒是挺好理解。当前是第i次操作,那么就是(i+1)x' >= ix,变形一下就是 x' >= x-[x/i+1],然后如果x<i+1,x就不会变了,所以就暴力做。题解说是o(sqrt(n))的,也没说怎么证明。。
1 #include<cstdio> 2 3 long long x, k; 4 int main() 5 { 6 int cas = 0; 7 while(scanf("%lld %lld", &x, &k)) 8 { 9 if (x == 0 && k == 0) break; 10 for (long long i = 1; i < k; i++){ 11 if (x < i+1) break; 12 x = x - x/(i+1); 13 } 14 printf("Case #%d: %lld\n", ++cas, x*k); 15 } 16 return 0; 17 }