AtCoder Beginner Contest 143
October 23, 2019
前三道签到
A. Curtain
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <functional> #include <map> #define M 1001 using namespace std; int main() { int A, B; cin >> A >> B; if (A - B * 2 <= 0) cout << 0 << endl; else cout << A - B * 2 << endl; return 0; }
B. TAKOYAKI FESTIVAL 2019
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <functional> #include <map> #define M 101 using namespace std; int d[M]; int main() { int n, sum = 0; cin >> n; for (int i = 1; i <= n; i++) cin >> d[i]; for (int i = 1; i <= n; i++) for (int j = i + 1; j <= n; j++) sum += d[i] * d[j]; cout << sum << endl; return 0; }
C. Slimes
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <functional> #include <map> #define M 101 using namespace std; int main() { int n, tot = 1; char ch,last; cin >> n; cin >> last; for (int i = 2; i <= n; i++) { cin >> ch; if (ch == last) continue; else { tot++; last = ch; } } cout << tot << endl; return 0; }
D. Triangles
从一个数组中选三个数,问最多能组成多少个三角形。
本来是很简单的题,先排序,然后枚举两条边,二分查找第三条边就行了,复杂度O(n2log n),结果二分写了半天,还没考虑到重复的情况,WA了3次,浪费了大量时间,真是醉了。早知道还不如用upper_bound。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <functional> #include <map> #define M 2001 long long L[M]; long long n; using namespace std; long long check(long long i, long long j) { long long ans = L[i] + L[j] - 1; long long l = j + 1, r = n; while (l <= r) { long long mid = (l + r) / 2; if (L[mid] == ans) { return mid; } else if (L[mid] < ans) l++; else r--; } return r; } int main() { long long tot = 0; cin >> n; for (long long i = 1; i <= n; i++) cin >> L[i]; sort(L + 1, L + n + 1); for (long long i = 1; i <= n - 2; i++) for (long long j = i + 1; j <= n - 1; j++) { long long num = 0; long long pos = check(i, j); for (long long k = 1; k <= n; k++) if (L[pos] == L[pos + k]) num++; else break; pos += num; tot += pos - j; } cout << tot << endl; return 0; }
upper_bound版:代码又少,速度还快,我佛辣。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <functional> #include <map> #define M 2001 long long L[M]; long long n; using namespace std; int main() { long long tot = 0; cin >> n; for (long long i = 1; i <= n; i++) cin >> L[i]; sort(L + 1, L + n + 1); for (long long i = 1; i <= n - 2; i++) for (long long j = i + 1; j <= n - 1; j++) { long long tmp = L[i] + L[j] - 1; long long pos = upper_bound(L + j + 1, L + n + 1, tmp) - L; pos -= 1; tot += pos - j; } cout << tot << endl; return 0; }
E. Travel by Car
考场上没时间想这道题了(虽然感觉有时间也想不出来)。题解的思路非常巧妙,如果要在路上加k次油,那么整条路径被加油点分为了k+1段,因此我们可以把每一段当成一个整体看待。具体的做法是先用Floyd求最短路,然后枚举所有路径,如果这条路径的长度小于等于L,就把它设置为1,如果大于就设置为Inf。然后再跑一遍Floyd,dis[from][to]-1就是加油的次数。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <functional> #include <map> #define M 301 #define MA 1e9+1 using namespace std; int dis[M][M]; int main() { int n, m, l; cin >> n >> m >> l; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) dis[i][j] = MA; for (int i = 1; i <= m; i++) { int from, to, cost; cin >> from >> to >> cost; dis[from][to] = dis[to][from] = cost; } for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (dis[i][j] <= l) dis[i][j] = 1; else dis[i][j] = MA; for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); int q; cin >> q; for (int i = 1; i <= q; i++) { int from, to; cin >> from >> to; if (dis[from][to] == MA) cout << -1 << endl; else cout << dis[from][to] - 1 << endl; } return 0; }
F. Distinct Numbers
给出一个长度为N的序列,求对于所有k∈[1,N] ,每次从序列中取出k个互不相同的数,最多能取多少次。
首先我们发现数字具体的数值是没有意义的,关键是其出现的次数,所以可以先用一个桶排序计数,然后把出现的次数排序得到数组a。假设取出k个数的操作数为x,那么一共会取出k*x个数。而理论上能取出的数量上限是:对于出现次数小于等于x的数,将其全部取出,对于出现次数大于x的数,每个取x个出来。显然k*x必定小于等于这个上限,我们要找的就是满足这个条件的最大的x,因此可以二分答案,即对于每个x,检查它是否满足这个条件,满足就放大,不满足就缩小。为了寻找a中小于等于x的数的总和,我们可以先求一个前缀和,再在a中二分查找它的位置,最后的复杂度是O(nlog2n)。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <functional> #include <map> #define M (long long)3e5 + 10 using namespace std; long long cnt[M], a[M], sum[M]; int main() { long long n, tot = 0; cin >> n; for (long long i = 1; i <= n; i++) { long long tmp; cin >> tmp; cnt[tmp]++; } for (long long i = 1; i <= n; i++) { if (cnt[i] != 0) a[++tot] = cnt[i]; } sort(a + 1, a + tot + 1); sum[1] = a[1]; for (long long i = 2; i <= tot; i++) sum[i] = sum[i - 1] + a[i]; //前缀和 for (long long k = 1; k <= n; k++) { long long l = 1, r = n; while (l <= r) { long long mid = (l + r) >> 1; long long pos = upper_bound(a + 1, a + tot + 1, mid) - a; //返回数组中第一个大于或等于被查数的值的下标 pos--; //小于等于mid的数的个数 if (sum[pos] + (tot - pos) * mid < k * mid) r = mid - 1; else l = mid + 1; } cout << r << endl; } return 0; }