[Codeforces Round#488]Div.2

时间:2023-03-09 15:00:22
[Codeforces Round#488]Div.2

总结

这是我无聊透顶肝到三点半的一场 cf ,结果还真够无聊的

这套题涵盖了英语题,语文题,模拟题。注重考查了选手的英语素养能力,语文阅读能力和精湛的模拟和枚举能力。是不可多得的一套好题。[Codeforces Round#488]Div.2

没什么单独拿题写博客的必要,就组在一起写个博客。提供翻译造福人类。


A. Fingerprints

Description

题库链接

给出长度为 \(n\) 的序列 \(A\) ,和大小为 \(m\) 的集合 \(B\) ,要求找到 \(A\) 中最长子序列使得只出现 \(B\) 中的元素。

\(1\leq n,m\leq 10\)

Solution

比较难,要用到数组。

Code

比较难,不会写。


B. Knights of a Polygonal Table

Description

题库链接

给 \(n\) 个二元组 \((p_i,c_i)\) 。对于每个组 \(i\) ,求 \(p_j < p_i\) 中 \(c_j\) 前 \(k\) ( \(k\) 给定)大的二元组 \(j\) 的 \(c_j\) 的和。每个组输出答案 + \(c_i\) 。

\(1\leq n\leq 10^5\)

Solution

按 \(p\) 排序,扫过去,用小根堆维护前面的 \(k\) 大 \(c\) 值。

Code

比较难,要用到优先队列。


C. Two Squares

Description

题库链接

给你两个正方形,一个与坐标轴平行,另一个斜 \(45^\circ\) 。求是否有公共部分。

|坐标| \(\leq 100\)

Solution

调 \(\text{PNPoly}\) 调了好久...结果后来发现坐标都是整数...

因为容易得到相交的坐标一定也是整数,直接模拟就好了,坐标范围不大,直接涂色。

Code

写得太丑了,就不给了。


D. Open Communication

Description

题库链接

两个人分别拥有一对互异的正整数,数值介于 \(1\sim 9\) 之间,现在两个人分别给出 \(n,m\) 对互异的正整数(数值也介于 \(1\sim 9\) 之间),他们拥有的那对数在这 \(n\) 组或 \(m\) 组中,现在他们并不知道自己的数是哪对,唯一确定的信息是原来拥有的那对数有且只有一个数是两个人均有的。现在你想知道:是否能求出两人均有的数;如果你不能确定,判断这两个人互相能否确定。

\(1\leq n,m\leq 12\)

Solution

我不知道我题面讲清楚没有...

转化一下模型,我们假设 \(n\) 组中的数对 \(i\) 与 \(m\) 组中的数对 \(j\) 是匹配的但且仅当 \(i\) \(j\) 两对数中有且只有一个数是相同的。

那么原问题就变成了:

  1. 判断每一组是否与另一边的每一组是否有且仅有一组匹配或者有多组匹配但相匹配的数值是相同的;
  2. 判断同一边的两组与另一边的组匹配的数是否相同

具体见代码吧...感觉这题还是非常奇怪...

Code

#include <bits/stdc++.h>
using namespace std; int n, m, a[20][2], b[20][2]; int common(int x, int y) {
int cnt = 0, ans;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
if (a[x][i] == b[y][j]) ++cnt, ans = a[x][i];
return cnt == 1 ? ans : 0;
}
void work() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d%d", &a[i][0], &a[i][1]);
for (int i = 1; i <= m; i++) scanf("%d%d", &b[i][0], &b[i][1]);
int ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
int x;
if (!(x = common(i, j))) continue;
for (int k = 1; k <= m; k++)
if (common(i, k) && common(i, k) != x) {puts("-1"); return; }
for (int k = 1; k <= n; k++)
if (common(k, j) && common(k, j) != x) {puts("-1"); return; }
if (!ans) ans = x;
else if (x != ans) {puts("0"); return; }
}
printf("%d\n", ans);
}
int main() {work(); return 0; }

E. Careful Maneuvering

Description

题库链接

在数轴上有两组整点点集大小为 \(n,m\) ,每组点的横坐标相等,纵坐标不一定相等,且两组点横坐标非 \(0\) 且互为相反数。

现在要求在 \(x=0\) 这条直线上放两个点,使得最多的点关于两个点任意一个能与另一部分对称。找到这个最大值。

\(1\leq n,m\leq 60\) , |纵坐标| \(\leq 10000\)

Solution

我们将左右两边点的纵坐标相加,可以得到 \(n\times m\) 组值,这些值中相等的就代表这些点对关于同一个点对称。注意到点不是很多,我们考虑去枚举对称点,计算两个对称点有关的互异点对个数。可以用 \(bitset\) 来处理。

Code

#include <bits/stdc++.h>
using namespace std; int n, m, a[100], b[100], s[4000], cnt;
bitset<130>c[40005]; void work() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), a[i] += 10000;
for (int i = 1; i <= m; i++) scanf("%d", &b[i]), b[i] += 10000;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
c[a[i]+b[j]][i] = 1, c[a[i]+b[j]][n+j] = 1, s[++cnt] = a[i]+b[j];
int ans = 0;
for (int i = 1; i <= cnt; i++)
for (int j = i; j <= cnt; j++)
ans = max(ans, int((c[s[i]]|c[s[j]]).count()));
printf("%d\n", ans);
}
int main() {work(); return 0; }

F. Compute Power

Description

题库链接

给出 \(n\) 组数对,每组数对有两个参数 \(a,b\) ,现在要求为这 \(n\) 个数对分组,要求:

  1. 一组最多有两个元素;
  2. \(a\) 相同的两个数对不能放在同一组

记方案分成的组数为 \(x\) 。对于每组,该组也有两个参数 \(A,B\),是这一组中 \(a\) 最大的数对的 \(a,b\) (若组内只有一个元素,那么就是这个元素的 \(a,b\) )。

要求

\[\frac{\sum_{i=1}^x A_i}{\sum_{i=1}^x B_i}\]

最小,求这个最小值。

\(1\leq n\leq 50\)

Solution

套路题啊。

分数规划,我们二分答案。先按 \(a\) 从大到小排序,相同则按 \(b\) 从大到小排序。

记 \(f_{i,j}\) 为前 \(i\) 个数对分了 \(j\) 组的答案最小值。

因为不能将 \(a\) 相同的放在同一组,所以我们对于 \(a\) 相同的数对一起处理。

转移的话就是考虑这些 \(a\) 相同的数有几个新开一组,其余的就放在那些只有一个元素的组里面。

由贪心的思想,对于 \(a\) 相同时, \(b\) 越大的越偏向于让他新开一组,所以之前要按 \(b\) 从大到小排序,这样就可以用前缀和来优化。

Code

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll inf; int n; ll f[65][65], sum[65];
struct tt {
ll a, b;
bool operator < (const tt &x) const {
return a == x.a ? b > x.b : a > x.a;
}
}p[65]; bool judge(ll mid) {
memset(f, 127, sizeof(f)); inf = f[0][0];
f[0][0] = 0;
for (int i = 1, loc; i <= n; i = loc+1) {
for (loc = i; p[loc+1].a == p[i].a; loc++);
sum[i-1] = 0;
for (int j = i; j <= loc; j++) sum[j] = sum[j-1]+p[j].b;
for (int j = 0; j <= n; j++) if (f[i-1][j] != inf) {
for (int k = max(0, (loc-i+1)-(2*j-(i-1))); k <= min(n-j, loc-i+1); k++) {
f[loc][j+k] = min(f[loc][j+k], f[i-1][j]+p[i].a*k-mid*(sum[i+k-1]-sum[i-1]));
}
}
}
for (int i = 0; i <= n; i++) if (f[n][i] <= 0) return true;
return false;
}
void work() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%I64d", &p[i].a), p[i].a *= 1000;
for (int i = 1; i <= n; i++) scanf("%I64d", &p[i].b);
sort(p+1, p+n+1);
ll l = 1, r = 1ll*100000000*1000, ans;
while (l <= r) {
ll mid = (l+r)>>1;
if (judge(mid)) r = mid-1, ans = mid;
else l = mid+1;
}
printf("%I64d\n", ans);
}
int main() {work(); return 0; }