USACO 2021~2022 比赛记录
USACO 2021~2022 比赛记录
前言
USACO 2020~2021 没写过这玩意,当时打到了金组,这个赛季来写一下。
USACO 2021~2022 DEC Gold
T1
不会。
试图搞出 但写挂了,最后只过了样例的 个点。
等同于啥都没过就不放代码了= =
T2
一开始(题还没读完的时候)莫名其妙有个念头:整体二分。当然这显然在胡扯。但是这个思路至关重要,使得我一上来就去想对于一个新的 对每个 会有什么贡献,直接推出了正解;而不是像我的其他同学一样去想对每个 分别求答案,最后做不出来。
开了个 Excel 手模样例,发现对于一个新的 ,假设 里询问过的小于 的最大的数为 ,大于 的最小的数为 (即前驱后继),那么 会将所有 分成四段:
- 若 :根据题意,这次询问会因为可以根据 的结果推出而被忽略,不会贡献答案。
- 若 :这部分的询问结果不能根据已有的结果推出,会进行询问,并贡献一个
HI
。 - 若 :这部分的询问结果不能根据已有的结果推出,会进行询问,并贡献一个
LO
。 - 若 :根据题意,这次询问会因为可以根据 的结果推出而被忽略,不会贡献答案。
于是问题转化为了设计一个数据结构,使得支持对于一个区间在字符串末尾添加 HI
或 LO
,并统计 HILO
个数。显然可以通过打标记的线段树维护。
过了 个点。
写得不太好看的赛时 AC 代码:
//By: Luogu@rui_er(122461)
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int n, p[N], pos[N], ps[N];
set<int> sps, sng;
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
enum Color {
UK = 0, HI, LO
};
struct Node {
int HILO;
Color tag;
};
struct SegTree {
Node t[N<<5];
#define lc(u) (u<<1)
#define rc(u) (u<<1|1)
void pushup(int u) {
t[u].HILO = t[lc(u)].HILO + t[rc(u)].HILO;
}
void pushdown(int u) {
if(t[u].HILO) {
t[lc(u)].HILO += t[u].HILO;
t[rc(u)].HILO += t[u].HILO;
t[u].HILO = 0;
}
if(t[u].tag == UK) return;
if(t[u].tag == HI) {
t[lc(u)].tag = HI;
t[rc(u)].tag = HI;
t[u].tag = UK;
}
else {
if(t[lc(u)].tag == HI) ++t[lc(u)].HILO;
if(t[rc(u)].tag == HI) ++t[rc(u)].HILO;
t[lc(u)].tag = LO;
t[rc(u)].tag = LO;
t[u].tag = UK;
}
}
void modify(int u, int l, int r, int ql, int qr, Color val) {
if(ql <= l && r <= qr) {
if(t[u].tag == HI && val == LO) {
++t[u].HILO;
t[lc(u)].tag = UK;
t[rc(u)].tag = UK;
}
t[u].tag = val;
return;
}
pushdown(u);
int mid = (l + r) >> 1;
if(ql <= mid) modify(lc(u), l, mid, ql, qr, val);
if(qr > mid) modify(rc(u), mid+1, r, ql, qr, val);
// pushup(u);
}
void print(int u, int l, int r) {
if(l == r) {
printf("%d\n", t[u].HILO);
return;
}
pushdown(u);
int mid = (l + r) >> 1;
print(lc(u), l, mid);
print(rc(u), mid+1, r);
}
}sgt;
int main() {
scanf("%d", &n);
rep(i, 1, n) {
scanf("%d", &p[i]);
pos[p[i]] = i;
}
ps[0] = pos[1];
ps[n] = pos[n];
rep(i, 1, n-1) ps[i] = max(pos[i], pos[i+1]);
rep(i, 1, n) {
auto lwit = sng.upper_bound(-p[i]);
auto upit = sps.upper_bound(p[i]);
int lw = lwit == sng.end() ? 0 : -*lwit;
int up = upit == sps.end() ? n : *upit;
// printf("i=%d lw=%d up=%d p=%d\n", i, lw, up, p[i]);
sgt.modify(1, 0, n, lw, p[i]-1, HI);
// printf("GIVEHI [%d, %d]\n", lw, p[i]-1);
sgt.modify(1, 0, n, p[i], up-1, LO);
// printf("GIVELO [%d, %d]\n", p[i], up-1);
sng.insert(-p[i]);
sps.insert(p[i]);
}
sgt.print(1, 0, n);
return 0;
}
T3
乱搞题,没啥技术含量。
首先如果一个手镯被分成了两个部分,即先后存在三束光线,使得这个手镯的状态是有、无、有,那么一定不符合要求。
然后如果两个手镯没有交叉,在任意一束光线处观察,都一定可以通过将每个手镯的两次出现替换为有权值的左、右括号(小括号、中括号、大括号、第四括号……),得到一个合法的括号序列。判掉了在一束光线处有交叉的情况。
还有就是如果两个手镯的位置关系变了,一定被分成了两个部分或有交叉。A 与 B 的位置关系包括:A 在 B 前面、A 在 B 后面、A 包含于 B、A 包含 B。每种情况开个 vector 或类似的东西维护即可。
交上去,发现过了大多数点,但少数点过不去。手模了几组数据,把自己 hack 了。
问题在于漏了一种情况:显然,如果手镯 A 包含于手镯 B,那么当 B 闭合时(即在一束光线中不再出现时),A 一定更早地闭合了,不可能有一束光线没有 B 但有 A,否则此时必然存在交叉。
加上这个判断即可,过了 个点。
写得不太好看的赛时 AC 代码:
//By: Luogu@rui_er(122461)
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
const int N = 105;
int T, n, m, k[N], buc[N][N], tmp[N], tmpv1[N];
vector<int> a[N];
stack<int> stkv1;
set<int> ins[N], aft[N];
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
bool verdictBracket(int id) {
rep(i, 1, n) tmpv1[i] = 0;
while(!stkv1.empty()) stkv1.pop();
for(auto i : a[id]) {
if(!tmpv1[i]) {
rep(j, 1, n) {
if(i == j) continue;
if(tmpv1[j] == 1) {
ins[j].insert(i);
if(ins[i].find(j) != ins[i].end()
|| aft[i].find(j) != aft[i].end()
|| aft[j].find(i) != aft[j].end())
return
// printf("VER! @%d.%d.%d\n", id, i, j)&
0;
}
if(tmpv1[j] == 2) {
aft[j].insert(i);
if(ins[i].find(j) != ins[i].end()
|| ins[j].find(i) != ins[j].end()
|| aft[i].find(j) != aft[i].end())
return
// printf("VER? @%d.%d.%d\n", id, i, j)&
0;
}
}
tmpv1[i] = 1;
stkv1.push(i);
}
else if(i == stkv1.top()) {
tmpv1[i] = 2;
stkv1.pop();
}
else return 0;
}
rep(i, 1, n) {
if(!tmpv1[i]) {
for(auto j : ins[i]) {
if(tmpv1[j]) return 0;
}
}
}
return 1;
}
#define give(result) do{puts(result);goto Init;}while(false)
int main() {
for(scanf("%d", &T);T;T--) {
scanf("%d%d", &n, &m);
rep(i, 1, m) {
scanf("%d", &k[i]);
a[i].resize(k[i]);
rep(j, 0, k[i]-1) {
scanf("%d", &a[i][j]);
++buc[i][a[i][j]];
}
}
rep(i, 1, m) {
if(!verdictBracket(i)) give("NO");
rep(j, 1, n) {
if(!tmp[j] && buc[i][j] == 2) ++tmp[j];
else if(tmp[j] == 1 && !buc[i][j]) ++tmp[j];
else if(tmp[j] == 2 && buc[i][j] == 2) ++tmp[j];
// printf("%d%c", tmp[j], " \n"[j==n]);
if(tmp[j] == 3) give("NO");
}
}
per(i, m, 1) if(!verdictBracket(i)) give("NO"); // rejudge it (reverse HACK)
give("YES");
Init:
rep(i, 1, m) {
k[i] = 0;
a[i].clear();
rep(j, 1, n) buc[i][j] = 0;
}
rep(i, 1, n) {
tmp[i] = 0;
ins[i].clear();
aft[i].clear();
}
n = m = 0;
}
return 0;
}
/*
HACKED (Verdict: WRONG_ANSWER)
1
4 2
4 1 2 2 1
2 2 2
Expected: NO
Found: YES
Rejudge: NO (Verdict: OK)
*/
总结
符合预期。还记得 USACO 2020~2021 OPEN Gold 我还啥都不会直接弃了呢。按照总满分为 分计,大概是得到了 分,貌似卡在了晋级/不晋级的中间,只能看人品了。。
upd:貌似上 P 失败了。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现