2017ACM/ICPC亚洲区沈阳站(部分解题报告)
题意
计算四个整数的和
解题思路
使用Java大整数
1 import java.math.BigInteger; 2 import java.util.Scanner; 3 4 /** 5 * 6 * @author reqaw 7 */ 8 public class Main { 9 10 /** 11 * @param args the command line arguments 12 */ 13 public static void main(String[] args) { 14 Scanner in = new Scanner(System.in); 15 int T; 16 T = in.nextInt(); 17 while(T-- > 0) { 18 BigInteger a = in.nextBigInteger(); 19 BigInteger b = in.nextBigInteger(); 20 BigInteger c = in.nextBigInteger(); 21 BigInteger d = in.nextBigInteger(); 22 System.out.println(a.add(b).add(c).add(d)); 23 } 24 } 25 26 }
题意
n只兔子排在一条直线上,每只兔子都有一个坐标,最外边的兔子可以跳到两只兔子中间的空位上,每跳一次称为一次计数,问最多能够玩几次。
解题思路
从第二只兔子开始计算相邻的空位有几个,正着一遍,倒着一遍,取最大值即可。
1 #include <cstdio> 2 3 const int maxn = 500 + 10; 4 int a[maxn]; 5 int main() 6 { 7 int T, n; 8 scanf("%d", &T); 9 while(T--) { 10 scanf("%d", &n); 11 for(int i = 0; i < n; i++) { 12 scanf("%d", &a[i]); 13 } 14 int s1 = 0; 15 for(int i = 1; i < n - 1; i++) { 16 s1 += a[i + 1] - a[i] - 1; 17 } 18 int s2 = 0; 19 for(int i = n - 2; i > 0; i--) { 20 s2 += a[i] - a[i - 1] - 1; 21 } 22 printf("%d\n", s1 > s2 ? s1 : s2); 23 } 24 return 0; 25 }
HDU 6222 Heron and His Triangle
题意
先定义了一个H三角形,它的三条边由连续的三个整数构成,即t-1, t , t+1,并且它的面积是一个整数。给出一个整数n(最大可能是10^30),问满足t>=n的最小t是多少。
解题思路
先暴力打出前几个满足条件的t,仔细观察发现满足一个规律,res[i] = res[i - 1] * 4 - res[i - 2];由于数字很大,使用Java大数。
1 /*先暴力出前几项*/ 2 #include <cstdio> 3 #include <cmath> 4 typedef unsigned long long ull; 5 int main() 6 { 7 for(ull t = 4; t <= 1000000; t++) { 8 ull a = t - 1; 9 ull b = t; 10 ull c = t + 1; 11 ull p = (a + b + c) / 2; 12 ull k = p * (p - a) * (p - b) * (p - c); 13 if((ull)sqrt(k) * (ull)sqrt(k) == k) { 14 printf("%lld\n", t); 15 } 16 } 17 return 0; 18 }
找到规律后打表
1 import java.math.BigInteger; 2 import java.util.Scanner; 3 4 /** 5 * 6 * @author reqaw 7 */ 8 public class Main { 9 10 /** 11 * @param args the command line arguments 12 */ 13 public static void main(String[] args) { 14 BigInteger res[] = new BigInteger[100]; 15 res[0] = BigInteger.valueOf(4L); 16 res[1] = BigInteger.valueOf(14L); 17 for(int i = 2; i < 100; i++) { 18 res[i] = res[i - 1].multiply(res[0]).subtract(res[i - 2]); 19 //System.out.println(res[i]); 20 } 21 Scanner cin = new Scanner(System.in); 22 int T = cin.nextInt(); 23 while(T-- > 0) { 24 BigInteger n = cin.nextBigInteger(); 25 for(int i = 0; i < 100; i++) { 26 if(res[i].compareTo(n) >= 0) { 27 System.out.println(res[i]); 28 break; 29 } 30 } 31 } 32 } 33 34 }
HDU 6219 Empty Convex Polygons
题意
给出n个点,求由这n个点组成的最大空凸包的面积是多少。
解题思路
使用求解最大空凸包的模板,基本思想是穷举所 要求解的空凸包的最低最左点(先保证最低,再保证最左)。
对于每一个穷举到的点v,进行动态规划,用opt[i][j]表示符合如下限制的凸包中的最大面积:
在凸包上v顺时针过来第一个点是i,并且i顺时针过来第一个点k不在i->j的左手域(k 可以等于 j)。
1 #include <cstdio> 2 #include <cmath> 3 #include <algorithm> 4 using namespace std; 5 6 const int maxn = 100; 7 const double zero = 1e-8; 8 9 struct Vector { 10 double x, y; 11 }; 12 13 inline Vector operator - (Vector a, Vector b) { 14 Vector c; 15 c.x = a.x - b.x; 16 c.y = a.y - b.y; 17 return c; 18 } 19 inline double Sqr(double a) { 20 return a * a; 21 } 22 inline int Sign(double a) { 23 if(fabs(a) <= zero) return 0; 24 return a < 0 ? -1 : 1; 25 } 26 inline bool operator < (Vector a, Vector b) { 27 return Sign(b.y - a.y) > 0 || Sign(b.y - a.y) == 0 && Sign(b.x - a.x) > 0; 28 } 29 inline double Max(double a, double b) { 30 return a > b ? a : b; 31 } 32 inline double Length(Vector a) { 33 return sqrt(Sqr(a.x) + Sqr(a.y)); 34 } 35 inline double Cross(Vector a, Vector b) { 36 return a.x * b.y - a.y * b.x; 37 } 38 39 Vector dot[maxn], List[maxn]; 40 double opt[maxn][maxn]; 41 int seq[maxn]; 42 int n, len; 43 double ans; 44 45 bool Compare(Vector a, Vector b) { 46 int temp = Sign(Cross(a, b)); 47 if(temp != 0) return temp > 0; 48 temp = Sign(Length(b) - Length(a)); 49 return temp > 0; 50 } 51 52 void Solve(int vv) { 53 int t, i, j, _len; 54 for(i = len = 0; i < n; i++) { 55 if(dot[vv] < dot[i]) 56 List[len++] = dot[i] - dot[vv]; 57 } 58 for(i = 0; i < len; i++) { 59 for(j = 0; j < len; j++) { 60 opt[i][j] = 0; 61 } 62 } 63 sort(List, List + len, Compare); 64 double v; 65 for(t = 1; t < len; t++) { 66 _len = 0; 67 for(i = t - 1; i >= 0 && Sign(Cross(List[t], List[i])) == 0; i--); 68 69 while(i >= 0) { 70 v = Cross(List[i], List[t]) / 2; 71 seq[_len++] = i; 72 for(j = i - 1; j >= 0 && Sign(Cross(List[i] - List[t], 73 List[j] - List[t])) > 0; j--); 74 if(j >= 0) 75 v += opt[i][j]; 76 ans = Max(ans, v); 77 opt[t][i] = v; 78 i = j; 79 } 80 for(i = _len - 2; i >= 0; i--) { 81 opt[t][seq[i]] = Max(opt[t][seq[i]], opt[t][seq[i + 1]]); 82 } 83 } 84 } 85 86 int i; 87 double Empty() { 88 ans = 0; 89 for(i = 0; i < n; i++) { 90 Solve(i); 91 } 92 return ans; 93 } 94 95 int main() 96 { 97 int T; 98 scanf("%d", &T); 99 while(T--) { 100 scanf("%d", &n); 101 for(i = 0; i < n; i++) { 102 scanf("%lf%lf", &dot[i].x, &dot[i].y); 103 } 104 printf("%.1lf\n", Empty()); 105 } 106 return 0; 107 }
题意
相对题意不太好理解,给出n个顶点的无根树,也即无向图,k种颜色,问将每一个顶点染成一种颜色,然后将同种颜色的顶点用最少的边连起来(以及每两点间是最短路)组成E,有k中颜色,也就是每一种染色方案有E1,E2...Ek,问它们的交集(边的交集)最大是多少
解题思路
关键是题意的转化,可以想象的是在一个无根树中,给每个点染上k种颜色中的一种,然后将颜色相同的点使用最短路连接起来,连接起来的边算刷一遍,最后求的就是所有的边中,次数被刷k次及其以上的边最多有多少条。
具体实现时,先明白如果该边满足条件,那么两端的点都需要大于k个才行,我们使用深搜计算每个节点的子孙数即可。
1 #include <cstdio> 2 #include <vector> 3 using namespace std; 4 5 const int maxn = 200000 + 10; 6 int n, k; 7 int ans; 8 vector<int> e[maxn]; 9 int num[maxn]; 10 11 void dfs(int u, int pre) { 12 num[u] = 1; 13 int eus = e[u].size(); 14 for(int i = 0; i < eus; i++) { 15 int v = e[u][i]; 16 if(v == pre) continue; 17 dfs(v, u); 18 19 num[u] += num[v];//递归返回时将u的子孙结点数加上 20 if(num[v] >= k && n - num[v] >= k) ans++; 21 } 22 } 23 24 int main() 25 { 26 int T; 27 scanf("%d", &T); 28 while(T--) { 29 scanf("%d%d", &n, &k); 30 for(int i = 0; i < maxn; i++) { 31 e[i].clear(); 32 } 33 for(int i = 0; i < n - 1; i++) { 34 int u, v; 35 scanf("%d%d", &u, &v); 36 e[u].push_back(v); 37 e[v].push_back(u); 38 } 39 ans = 0; 40 dfs(1, -1); 41 printf("%d\n", ans); 42 } 43 return 0; 44 }