USACO 2021~2022 比赛记录

USACO 2021~2022 比赛记录

前言

USACO 2020~2021 没写过这玩意,当时打到了金组,这个赛季来写一下。

USACO 2021~2022 DEC Gold

T1

不会。

试图搞出 \(T=1\) 但写挂了,最后只过了样例的 \(3/20\) 个点。

等同于啥都没过就不放代码了= =

T2

一开始(题还没读完的时候)莫名其妙有个念头:整体二分。当然这显然在胡扯。但是这个思路至关重要,使得我一上来就去想对于一个新的 \(p_i\) 对每个 \(x+0.5\) 会有什么贡献,直接推出了正解;而不是像我的其他同学一样去想对每个 \(x+0.5\) 分别求答案,最后做不出来。

开了个 Excel 手模样例,发现对于一个新的 \(p_i\),假设 \(p_1\sim p_{i-1}\) 里询问过的小于 \(p_i\) 的最大的数为 \(pre\),大于 \(p_i\) 的最小的数为 \(suc\)(即前驱后继),那么 \(pre,p_i,suc\) 会将所有 \(x+0.5\) 分成四段:

  1. \(x+0.5 < pre\):根据题意,这次询问会因为可以根据 \(pre\) 的结果推出而被忽略,不会贡献答案。
  2. \(pre < x+0.5 < p_i\):这部分的询问结果不能根据已有的结果推出,会进行询问,并贡献一个 HI
  3. \(p_i < x+0.5 < suc\):这部分的询问结果不能根据已有的结果推出,会进行询问,并贡献一个 LO
  4. \(suc < x+0.5\):根据题意,这次询问会因为可以根据 \(suc\) 的结果推出而被忽略,不会贡献答案。

于是问题转化为了设计一个数据结构,使得支持对于一个区间在字符串末尾添加 HILO,并统计 HILO 个数。显然可以通过打标记的线段树维护。

过了 \(20/20\) 个点。

写得不太好看的赛时 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,否则此时必然存在交叉。

加上这个判断即可,过了 \(20/20\) 个点。

写得不太好看的赛时 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 我还啥都不会直接弃了呢。按照总满分为 \(1000\) 分计,大概是得到了 \(716.67\) 分,貌似卡在了晋级/不晋级的中间,只能看人品了。。

upd:貌似上 P 失败了。。

posted @ 2021-12-22 21:22  rui_er  阅读(204)  评论(3编辑  收藏  举报