Codeforces Round #287 (Div. 2) 解题报告
好久没写题了,底下代码都比较糟糕,将就着看吧。。
507A Amr and Music
要学最多的乐器,所以我们贪心选择时间花费少的。注意这里可以直接用pair,就不必写struct的compare函数了
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 6 struct ins{ 7 int v, id; 8 } a[110]; 9 bool cmp(ins a, ins b){ 10 return a.v < b.v; 11 } 12 int n, k, cnt; 13 14 int main() 15 { 16 scanf("%d %d", &n, &k); 17 for (int i = 0; i < n; i++){ 18 scanf("%d", &a[i].v); 19 a[i].id = i; 20 } 21 sort(a, a+n, cmp); 22 cnt = 0; 23 int j; 24 for (j = 0; j < n; j++){ 25 cnt += a[j].v; 26 if (cnt > k) break; 27 } 28 printf("%d\n", j); 29 for (int p = 0; p < j; p++) 30 printf("%d%c", a[p].id+1, p==j-1?'\n':' '); 31 return 0; 32 }
507B Amr and Pins
每次以圆周上某位置为中心,任意旋转。通过此操作最多让圆心向任意方向前进2r的距离,也就是说一次step,可以向目标前进d(0<d<=2r)地距离,所以答案就是ceil(dist/r/2)。
因为怕精度的问题,我直接用平方做的比较,结果就WA了几次。。本来是if(dist % (r*2)) ans++,我写的 if (dist^2 % (4*r^2)) ans++,实际上两者不等价。。本来是无法整除的,平方后可能可以整除。结果后来写的二分。。因为平方太大还爆longlong了。。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cmath> 5 using namespace std; 6 7 int r, x, y, xx, yy; 8 9 int main() 10 { 11 scanf("%d %d %d %d %d", &r, &x, &y, &xx, &yy); 12 long long dis2 = (long long)(xx - x) * (xx - x) 13 +(long long)(yy - y) * (yy - y); 14 long long ll = 0, rr = 150000, ans = 0; 15 while(ll <= rr){ 16 long long m = (ll + rr) / 2; 17 long long d = (m*r*2); 18 long long tmpd, tmpdis2; 19 if (d > 2100000000){ 20 tmpd = d; 21 tmpdis2 = sqrt(double(dis2)); 22 } 23 else tmpd = d * d, tmpdis2 = dis2; 24 if (tmpd >= tmpdis2){ 25 ans = m; 26 rr = m - 1; 27 } 28 else ll = m + 1; 29 } 30 cout << ans << endl; 31 return 0; 32 }
瞥了眼tourist的代码,虽然不是最简单的写法,但感觉写法挺值得借鉴的(对于这题,为什么对,我也没想太清楚。。)
1 int main() { 2 long long r, x, y, xx, yy; 3 cin >> r >> x >> y >> xx >> yy; 4 long long dist = (x - xx) * (x - xx) + (y - yy) * (y - yy); 5 long long step = 4 * r * r; 6 long long res = (dist + step - 1) / step; //整数向上取整的除法 7 long long ans = sqrt(1.0 * res); //整数向上取整的开根号 8 while (ans * ans < res) ans++; 9 while (ans > 0 && (ans - 1) * (ans - 1) >= res) ans--; 10 cout << ans << endl; 11 return 0; 12 }
507C Guess Your Way Out!
题意:给一颗高度为h的满二叉树(0..h),第n个叶子(1<=n<=2^h)为出口,按照LRLR的序列以及一定规则来遍历树,问找到出口前访问过多少个点。
分析:按照所给定的规则,走错一步,我们将访问完整棵子树,所以答案要多加子树的点数。实际上走向出口的正确序列可以直接由n-1的二进制表示得到,比照正确序列和LRLRLR,错的时候加上多余的点就可以了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 using namespace std; 6 7 int h, flag; 8 int p[110]; 9 long long n, ans, add; 10 int main() 11 { 12 scanf("%d %I64d", &h, &n); 13 n--; ans = 0; flag = 0; 14 add = 1; for (int i = 0; i < h; i++) add = add * 2; 15 int len = 0; memset(p, 0, sizeof(p)); 16 while(n){ 17 p[len++] = n & 1; 18 n = n / 2; 19 } 20 for (int i = h-1; i >= 0; i--){ 21 if (p[i] != flag) ans = ans + add; //flag为0向左,为1向右 22 else{ 23 ans ++; 24 flag ^= 1; //走对时才调整方向 25 } 26 add = add / 2; 27 // cout << ans << ' ' << i << ' ' << add << endl; 28 } 29 cout << ans << endl; 30 return 0; 31 }
507D The Maths Lecture
题意:给定n,m,k,问长度为n,没有前导0的数中,有多少个满足:存在某个后缀y, y>0且无前导0, 满足y mod k = 0。输出满足条件的数的个数mod m
分析:数位dp。dp[i][j]表示长度为i,无前导0的数,且没有后缀y能满足y mod k = 0,且本身mod k = j 的数的个数。这样就可以通过不断在左侧加上新的数,进行转移,对于mod k = 0的数,即dp[i][0],就可以累加他们到答案里。
然后需要注意,已经mod k=0的就不用转移了,否则会重复计算(也就满足dp[][]定义),从而导致全为0的后缀会被遗漏,还有p000suffix,这种中间有某些位为0的也会被遗漏,所以修改dp[i][j]的定义为长度<=i的自然数(不包括0)...然后就可以了
实际上,最后dp[i][j]表示长度不超过i的自然数(不包括0),没有后缀满足mod k = 0,自身mod k = j 的数的个数。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 6 long long n, m, k, ans; 7 long long dp[1100][110]; 8 9 int main() 10 { 11 cin >> n >> k >> m; 12 for (int i = 1; i < 10; i++) dp[1][i%k] ++; //初始化长度为1的,注意没有0 13 long long f = 1; 14 for (int i = 1; i < n; i++){ 15 f = f * 10 % k; //10的幂次用f来代替 16 for (int j = 0; j < k; j++){ 17 int add = j==0? 1: dp[i][j]; //之前为0的只能加上0000.. 18 for (int p = 1; p < 10; p++){ 19 dp[i+1][(j+p*f)%k] += add; 20 dp[i+1][(j+p*f)%k] %= m; 21 } 22 } 23 for (int j = 1; j < k; j++){ 24 dp[i+1][j] += dp[i][j]; 25 dp[i+1][j] %= m; 26 } 27 } 28 ans = dp[n][0] % m; f = 9; 29 for (int i = n-1; i > 0; i--){ 30 ans += dp[i][0] * f % m; 31 ans %= m; f = f * 10 % m; 32 } 33 cout << ans << endl; 34 return 0; 35 }
507E Breaking Good
题意:给一个n点m边的连通无向图,每个边有0和1两种属性,需要你求1到n的最短路,以及:影响的道路数 num = 不在最短路上且属性为1的边的个数+在最短路上且属性为0的边的个数 尽量少。
分析:tot[i]表示属性为i的边的总数,used[i]表示最短路上属性为i的边的个数,dis[i]表示到i点的最短路,所以num = (tot[1] - used[1]) + (dis[n] - used[1]),为了num尽量小,used[1]要尽量大。所以就是两个关键字的最短路了。
代码较丑,轻拍
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<set> 5 using namespace std; 6 7 8 struct edge{ 9 int v, w, n; 10 } e[200100]; 11 12 int esize; 13 int en[100100]; 14 void insert(int x, int y, int z){ 15 e[esize].v = y; 16 e[esize].w = z; 17 e[esize].n = en[x]; 18 en[x] = esize++; 19 } 20 21 22 int n, m; 23 int tot[2]; 24 int d[100100], p[100100], c[100100]; bool vis[100100]; 25 int q[8000000]; 26 int aa[100100], bb[100100], cc[100100]; 27 void spfa(){ 28 int head, tail; 29 head = tail = 0; 30 memset(d, 127, sizeof(int)*(n+1)); 31 memset(vis, 0, sizeof(bool)*(n+1)); 32 p[1] = d[1] = c[1] = 0; vis[1] = 1; 33 q[tail++] = 1; 34 while(head != tail){ 35 int u = q[head++]; 36 for (int t = en[u]; t != -1; t = e[t].n){ 37 int v = e[t].v; 38 if (d[v] > d[u] + 1 || ((d[v] == d[u]+1) && (c[v] < c[u] + e[t].w))){ 39 d[v] = d[u] + 1; 40 c[v] = c[u] + e[t].w; 41 p[v] = u; 42 if (!vis[v]){ 43 vis[v] = true; 44 q[tail++] = v; 45 } 46 } 47 } 48 vis[u] = false; 49 } 50 printf("%d\n", d[n]-c[n]+tot[1]-c[n]); 51 set<pair<int, int> > s; 52 int cur = n, pre; 53 while(cur != 1){ 54 pre = cur; cur = p[cur]; 55 s.insert(make_pair(cur, pre)); 56 s.insert(make_pair(pre, cur)); 57 } 58 for (int i = 0; i < m; i++){ 59 if (s.find(make_pair(aa[i], bb[i])) != s.end()){ 60 if (cc[i] == 0) printf("%d %d 1\n", aa[i], bb[i]); 61 } 62 else if (cc[i] == 1) printf("%d %d 0\n", aa[i], bb[i]); 63 } 64 65 } 66 int main() 67 { 68 scanf("%d %d", &n, &m); 69 esize = 0; memset(en, -1, sizeof(int) * (n+1)); 70 tot[0] = tot[1] = 0; 71 for (int i = 0, x, y, z; i < m; i++){ 72 scanf("%d %d %d", &x, &y, &z); 73 tot[z] ++; 74 insert(x, y, z); 75 insert(y, x, z); 76 aa[i] = x, bb[i] = y, cc[i] = z; 77 } 78 spfa(); 79 return 0; 80 }