【比赛链接】
【A】Fingerprints
- 【题意】给出一个包含 0 0 ~的序列,标记其中的一些数,按顺序输出序列中标记的数。
- 【题解】按题意模拟即可。
- 时间复杂度 O(N) O ( N )
- 【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 13
using namespace std;
int n, m, a[MAXN], has[MAXN];
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
int main(){
read(n), read(m);
for (int i = 1; i <= n; ++i)
read(a[i]);
for (int i = 1; i <= m; ++i){
int t; read(t);
has[t] = 1;
}
int fla = 1;
for (int i = 1; i <= n; ++i)
if (has[a[i]]) {
if (fla) {printf("%d", a[i]); fla = 0;}
else printf(" %d", a[i]);
}
printf("\n");
return 0;
}
【B】Knights of a Polygonal Table
- 【题意】给出 n n 个骑士,每个骑士有两个值,力量值和拥有的硬币数,保证每个骑士的力量值不同。每个骑士最多可以攻击其他个力量值比他小的骑士并获得他们的硬币。求每个骑士最多能拥有多少硬币,询问互不影响。
- 【题解】将所有骑士按照力量值从小到大排序,那么问题就转化为了:求在每个骑士前面的硬币数中前 k k 大之和,从前向后用堆维护一下即可。
- 时间复杂度
- 【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 100010
using namespace std;
int n, k;
LL ans[MAXN];
struct info{int p, c, id;}a[MAXN];
priority_queue <info> heap, tmp;
bool operator < (info a, info b){
return a.c > b.c;
}
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
bool cmp(info a, info b){
return a.p < b.p;
}
int main(){
read(n), read(k);
for (int i = 1; i <= n; ++i)
read(a[i].p);
for (int i = 1; i <= n; ++i)
read(a[i].c), a[i].id = i;
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; ++i){
ans[a[i].id] = a[i].c;
while (!heap.empty()){
info cur = heap.top();
ans[a[i].id] += cur.c;
tmp.push(cur);
heap.pop();
}
while (!tmp.empty()){
heap.push(tmp.top());
tmp.pop();
}
if ((int)heap.size() < k) heap.push(a[i]);
else if ((!heap.empty()) && a[i].c > heap.top().c) {
heap.pop();
heap.push(a[i]);
}
}
for (int i = 1; i <= n; ++i)
printf("%I64d%c", ans[i], " \n"[i == n]);
return 0;
}
【C】Two Squares
- 【题意】给出两个正方形,一个一个边与坐标轴平行,另一个边与坐标轴成 45 45 度角,问这两个正方形是否相交。
- 【题解】其实本题有更简单的做法,但因为本题是JSOI2018Round2Day2T1部落战争的一部分,所以就直接把暴力拿过来改一改用了。大概思路就是判一下每两条边是否有交,每个顶点是否在另一个凸多边形形内。本方法不仅适用于题目给出的条件,事实上适用于所有的凸多边形。
- 时间复杂度 O(NM) O ( N M ) ( N N 和分别为两凸多边形的边数,在本题中都为 4 4 )
- 【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
int fla;
struct vec{int x, y;}a[10], b[10];
int operator * (vec a, vec b){return a.x * b.y - a.y * b.x;}
vec operator - (vec a, vec b){return (vec){a.x - b.x, a.y - b.y};}
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
int main(){
fla = 0;
for (int i = 0; i < 4; ++i)
read(a[i].x), read(a[i].y);
a[4] = a[0];
for (int i = 0; i < 4; ++i)
read(b[i].x), read(b[i].y);
b[4] = b[0];
for (int i = 0; i < 4; ++i){
if (fla) break;
for (int j = 0; j < 4; ++j){
int k = (b[j].y - b[j + 1].y) / (b[j].x - b[j + 1].x), t = b[j].y - k * b[j].x,
opt = (a[i].x == a[i + 1].x), tmp;
if (opt == 1){
tmp = a[i].x;
int yy = k * tmp + t;
if (yy <= max(b[j].y, b[j + 1].y) && yy >= min(b[j].y, b[j + 1].y) && yy <= max(a[i].y, a[i + 1].y) && yy >= min(a[i].y, a[i + 1].y)) {fla = 1; break;}
} else {
tmp = a[i].y;
int xx = (tmp - t) / k;
if (xx <= max(b[j].x, b[j + 1].x) && xx >= min(b[j].x, b[j + 1].x) && xx <= max(a[i].x, a[i + 1].x) && xx >= min(a[i].x, a[i + 1].x)) {fla = 1; break;}
}
}
}
for (int i = 0; i < 4; ++i){
if (fla) break;
int f = 1, tnp = 0;
for (int j = 0; j < 4; ++j){
int tmp = (b[j] - a[i]) * (b[j + 1] - a[i]);
if (tmp == 0) {
f = 0; break;
} else {
if (!tnp) {if (tmp > 0) tnp = 1; else tnp = -1;}
else if (tmp * tnp < 0) {f = 0; break;}
}
}
if (f) fla = 1;
}
for (int i = 0; i < 4; ++i){
if (fla) break;
int f = 1, tnp = 0;
for (int j = 0; j < 4; ++j){
int tmp = (a[j] - b[i]) * (a[j + 1] - b[i]);
if (tmp == 0) {
f = 0; break;
} else {
if (!tnp) tnp = tmp;
else if (tmp * tnp < 0) {f = 0; break;}
}
}
if (f) fla = 1;
}
if (fla) printf("YES\n");
else printf("NO\n");
return 0;
}
【D】Open Communication
- 【题意】现在有一些pair,每个pair包含两个~ 9 9 之间的数,且保证两个数不相同,这里认为和 <y,x> < y , x > <script type="math/tex" id="MathJax-Element-6795"> </script>是完全相同的。有两个人,每人手中有一个pair,保证其中有且只有一个数是相同的,且两个人都不知道这个数是什么也不知道对方手里的是什么。两人相互沟通了一些pair,其中包括他们手中的。判定两人是否都得知那个相同的数,其他局外人是否能得知那个相同的数,若都能知道,则输出那个数。
- 【题解】若一个人手中存在某个pair,这个pair中的两个数都能找到一个匹配的pair,那么两人无法都得知那个数。若存在匹配的数超过一个,则其他局外人无法得知那个数。否则,输出这个数即可。
- 时间复杂度 O(NM) O ( N M )
- 【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 20
using namespace std;
int n, m, has[10], hasI[MAXN][2], hasII[MAXN][2];
struct info{int x, y;}a[MAXN], b[MAXN];
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
int get(int i, int j){
if ((a[i].x == b[j].x) && (a[i].y != b[j].y)) return a[i].x;
if ((a[i].x == b[j].y) && (a[i].y != b[j].x)) return a[i].x;
if ((a[i].y == b[j].x) && (a[i].x != b[j].y)) return a[i].y;
if ((a[i].y == b[j].y) && (a[i].x != b[j].x)) return a[i].y;
return 0;
}
int main(){
read(n), read(m);
for (int i = 1; i <= n; ++i)
read(a[i].x), read(a[i].y);
for (int i = 1; i <= m; ++i)
read(b[i].x), read(b[i].y);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j){
int tmp = get(i, j);
if (tmp) ++has[tmp], ++hasI[i][(tmp == a[i].x)], ++hasII[j][(tmp == b[j].x)];
}
for (int i = 1; i <= n; ++i)
if (hasI[i][0] && hasI[i][1]) {printf("-1\n"); return 0;}
for (int i = 1; i <= m; ++i)
if (hasII[i][0] && hasII[i][1]) {printf("-1\n"); return 0;}
int tmp = 0, tnp = 0;
for (int i = 1; i <= 9; ++i)
if (has[i]) ++tmp, tnp = i;
if (tmp > 1) {printf("0\n"); return 0;}
printf("%d\n", tnp);
return 0;
}
【E】Careful Maneuvering
- 【题意】有两艘太空飞船,由两组敌人包围,一组敌人的 x x 坐标等于,而另一组的 x x 坐标等于,且所有敌人的y坐标均为整数。两组中的每个敌人都将同时发射两条射线,每一条朝向一艘太空飞船。现在太空飞船想要在 x=0 x = 0 的某些位置( y y 坐标不一定是整数)定位自己,使得射到它们的射线摧毁尽可能多的敌人。若敌人无法避免激光照射,求最多能消灭多少敌人。
- 【题解】求一下中间每个的点能打到哪些敌人,然后枚举一下太空飞船在哪两个位置,把能打到的敌人求个并再求一下个数。时间限制好像有点卡哦,那拿bitset卡一卡就好了。
- 时间复杂度 O(N5) O ( N 5 ) ( N N ,同阶)
- 【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 70
#define MAXM 10000
using namespace std;
int n, m, a[MAXN], b[MAXN], has[MAXM * 4 + 10], ans, q[MAXN * MAXN], cnt;
bitset<MAXN * 2 + 10> c[MAXM * 4 + 10], tmp;
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
int main(){
read(n), read(m);
for (int i = 1; i <= n; ++i)
read(a[i]), a[i] += MAXM;
for (int i = 1; i <= m; ++i)
read(b[i]), b[i] += MAXM;
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;
if (!has[a[i] + b[j]]) has[a[i] + b[j]] = 1, q[++cnt] = a[i] + b[j];
}
for (int i = 1; i <= cnt; ++i){
chkmax(ans, (int)c[q[i]].count());
for (int j = i + 1; j <= cnt; ++j){
tmp = c[q[i]] | c[q[j]];
chkmax(ans, (int)tmp.count());
}
}
printf("%d\n", ans);
return 0;
}
【F】Compute Power
- 【题意】有 n n 个任务,每个任务有两个值,消耗和处理器数量 bi b i 。这些任务要分配到数量无限制的计算机上处理,每台计算机最多可以处理两个任务,且第一个任务的消耗严格大于第二个任务的消耗。最小化并输出以下这个值:执行第一个任务期间,所有计算机上当前运行的任务的消耗的总和除以当前所使用的处理器的总数量,即 ∑ai∑bi ∑ a i ∑ b i ,其中任务 i i 是某电脑第一个任务。
- 【题解】首先发现,如果对于某值可行,那么对于所有更大的值也是可行的,答案具有可二分性。二分答案,问题转化为判定性问题:判断是否存在一种可行的分配方案,使得要求的值小于等于当前二分值。设当前二分值为mid,则需要,移项化简,即需要 ∑ai−mid∗∑bi≤0 ∑ a i − m i d ∗ ∑ b i ≤ 0 ,那么我们可以考虑最小化 ∑ai−mid∗∑bi ∑ a i − m i d ∗ ∑ b i 这个值,判断是否小于等于零。那么我们尝试用DP求解最小值。将所有任务按照消耗为第一关键字从大到小, ai−mid∗bi a i − m i d ∗ b i 为第二关键字从大到小排序,那么就可以设DP状态 f[i][j] f [ i ] [ j ] 表示当前处理到第 i i 种消耗值,有台计算机有且仅有一个任务,且该任务的消耗值大于等于目前处理的消耗值。枚举目前的消耗值中有几个任务放在某计算机的第二个任务处理,将其他任务的值加入DP值中转移即可。
- 时间复杂度 O(N2logPRECISION) O ( N 2 l o g P R E C I S I O N )
- 【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 70
using namespace std;
int n;
LL a[MAXN], b[MAXN], f[MAXN][MAXN];
struct info{LL x, y;}c[MAXN];
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
bool cmp(info a, info b){
if (a.x != b.x) return a.x > b.x;
else return a.y > b.y;
}
bool ok(LL x){
for (int i = 1; i <= n; ++i)
c[i].x = a[i], c[i].y = a[i] - x * b[i];
sort(c + 1, c + 1 + n, cmp);
for (int i = 2; i <= n; ++i)
c[i].y += c[i - 1].y;
int L = 1, R = 1, cnt = 0;
f[0][0] = 0;
while (L <= n){
++cnt;
R = L;
while (R < n && c[R].x == c[R + 1].x) ++R;
for (int i = 0; i < L; ++i){
if (f[cnt - 1][i] == 1ll * INF * INF) continue;
for (int j = L - 1; j <= R; ++j)
if (j - L + 1 <= i)
chkmin(f[cnt][i - (j - L + 1) + R - j], f[cnt - 1][i] + c[R].y - c[j].y);
}
L = R + 1;
}
int ret = 0;
for (int i = 0; i <= n; ++i)
if (f[cnt][i] <= 0) ret = 1;
for (int i = 0; i <= cnt; ++i)
for (int j = 0; j <= n; ++j)
f[i][j] = 1ll * INF * INF;
return ret;
}
int main(){
read(n);
LL l = 0, r = 0;
for (int i = 1; i <= n; ++i)
read(a[i]), a[i] = a[i] * 1000ll, r += a[i];
for (int i = 1; i <= n; ++i)
read(b[i]);
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= n; ++j)
f[i][j] = 1ll * INF * INF;
while (l + 1 < r){
LL mid = (l + r) / 2;
if (ok(mid)) r = mid;
else l = mid;
}
printf("%I64d\n", r);
return 0;
}