← 全部文章

Ozon Tech Challenge 2020 (Div.1 + Div.2, Rated, T-shirts + prizes!) 题解

本文目录(8 节)
  1. Kuroni and the Gifts
  2. Kuroni and Simple Strings
  3. Kuroni and Impossible Calculation
  4. Kuroni and the Celebration
  5. Kuroni and the Score Distribution
  6. Kuroni and the Punishment
  7. Kuroni and Antihype
  8. Kuroni the Private Tutor

Ozon Tech Challenge 2020 (Div.1 + Div.2, Rated, T-shirts + prizes!)

Kuroni and the Gifts

由于 a,ba,b 分别互不相同,因此想让 a+ba+b 互不相同,对 a,ba,b 分别排序即可。

const int N = 107;
int n, a[N], b[N];

inline void solve() {
	rd(n), rda(a, n), rda(b, n);
	sort(a + 1, a + n + 1);
	sort(b + 1, b + n + 1);
	for (int i = 1; i <= n; i++)
		print(a[i], " \n"[i==n]);
	for (int i = 1; i <= n; i++)
		print(b[i], " \n"[i==n]);
}

int main() {
	int T;
	rd(T);
	while (T--) solve();
	return 0;
}

Kuroni and Simple Strings

要求最终不能再存在 () 这样的子串,因此只能是若干个 ) 后接若干个 (。

因此我们要找到一个位置,这个位置左边的 ( 全部被删掉,右边的 ) 全部被删掉。

由于只能删掉同等数量的 ( 和 ),而这个位置一定存在,因此答案要么为 00,要么为 11。

const int N = 1e3 + 7;
int n, a[N], b[N];
char s[N];

int main() {
	rds(s, n);
	vi ans;
	for (int i = 1; i <= n; i++)
		a[i] = a[i-1] + (s[i] == '(');
	for (int i = n; i; i--)
		b[i] = b[i+1] + (s[i] == ')');
	for (int i = 0; i <= n; i++)
		if (a[i] == b[i+1]) {
			for (int j = 1; j <= i; j++)
				if (s[j] == '(') ans.pb(j);
			for (int j = i + 1; j <= n; j++)
				if (s[j] == ')') ans.pb(j);
			if (!ans.size()) {
				print(0);
				return 0;
			}
			print(1);
			print(ans.size());
			for (int x : ans) print(x, ' ');
			return 0;
		}
	return 0;
}

Kuroni and Impossible Calculation

这个绝对值看起来不太好搞,那么我们排序一下,就一定是后面减前面了。

依次考虑每个数与前面的数对答案的贡献,但我们显然不能 O(n2)\mathcal O(n^2) 枚举。

注意到这个模数非常小,因此我们对前面的数开一个桶,记录每个余数的个数。

那么我们只需要 O(nm)\mathcal O(nm) 就可以求出每个可能的差的数量,最后快速幂一下就能求出答案了。

什么,你问 O(nm)\mathcal O(nm) 不是 2×1082 \times 10^8 吗,怎么能过?

你要相信 CF 神机一秒跑十亿不成问题。

const int N = 2e5 + 7, M = 1e3 + 7;
int n, a[N], c[M];
ll f[M];
modint ans = 1;

int main() {
	rd(n), rd(P), rda(a, n);
	sort(a + 1, a + n + 1);
	for (int i = 1; i <= n; i++) {
		a[i] %= P;
		for (int j = 0; j < P; j++)
			f[(a[i]-j+P)%P] += c[j];
		c[a[i]]++;
	}
	for (int i = 0; i < P; i++) ans *= (modint)i ^ f[i];
	print(ans);
	return 0;
}

Kuroni and the Celebration

每次询问两个点的 LCA,最多询问 ⌊n2⌋\lfloor \frac{n}{2} \rfloor 次确定根。

很傻的一道交互题。

注意到如果一个叶子和另一个点的 LCA 就是这个叶子,那么这个叶子一定为根;如果不是,则这个叶子一定不是根。

那么我们每次询问两个叶子的 LCA 即可,如果是其中某一个点,则那个点就是根;否则删掉两个点,注意可能产生新的叶子。

最坏情况下,每次询问会删掉两个叶子,那么 ⌊n2⌋\lfloor \frac{n}{2} \rfloor 次后最多只剩下一个点,这个点显然就是根了。

const int N = 1e3 + 7;
int n, d[N], v[N];
vi e[N];

int main() {
	ios::sync_with_stdio(0);
	cin >> n;
	for (int i = 1, x, y; i < n; i++)
		cin >> x >> y, e[x].pb(y), e[y].pb(x), d[x]++, d[y]++;
	queue<int> q;
	for (int i = 1; i <= n; i++)
		if (d[i] == 1) q.push(i);
	for (int i = 1; i <= n / 2; i++) {
		int x = q.front();
		q.pop();
		int y = q.front();
		q.pop();
		cout << "? " << x << " " << y << endl;
		fflush(stdout);
		int z;
		cin >> z;
		if (z == x || z == y) {
			cout << "! " << z << endl;
			return 0;
		}
		for (int z : e[x])
			if (!v[z] && (--d[z] == 1)) q.push(z);
		for (int z : e[y])
			if (!v[z] && (--d[z] == 1)) q.push(z);
		v[x] = v[y] = 1;
	}
	for (int i = 1; i <= n; i++)
		if (!v[i]) cout << "! " << i << endl;
	return 0;
}

Kuroni and the Score Distribution

要求构造一个长度为 nn 的单调上升序列 aa,使得恰好有 mm 个三元组 (i,j,k)(i<j<k)(i,j,k)(i < j < k) 满足 ai+aj=aka_i + a_j = a_k。

很傻的一道构造题。

先 1,2,3,4,5,…1,2,3,4,5,\dots 的填,产生的三元组个数为 0,0,1,1,2,…0,0,1,1,2,\dots。

一直填到第一次个数 ≥m\ge m 的位置,假设多了 kk 个。

注意到在这个位置上的数每增加 22,个数会减 11,因此这个位置上的数加上 2k2k 即可。

后面的不应该再存在满足条件的三元组了,那么填一定间隔下最大的几个数就够了。

如果填完 nn 个数,三元组的数量依旧 <m< m,则说明无解,因为这个填数的方法最大化了三元组的数量。

const int N = 5e3 + 7;
int n, m, a[N];

int main() {
	rd(n), rd(m);
	int t = 0;
	for (int i = 0; i < n; i++) {
		a[i] = i + 1;
		t += i >> 1;
		if (t >= m) {
			a[i] += (t - m) << 1;
			for (int j = n - 1, k = 1e9; j > i; j--, k -= i + 2)
				a[j] = k;
			for (int j = 0; j < n; j++)
				print(a[j], " \n"[j==n]);
			return 0;
		}
	}
	print(-1);
	return 0;
}

Kuroni and the Punishment

首先可以知道答案一定 ≤n\le n,这个界还可以再紧一点,一定 ≤\le 奇数的个数,设为 tt。

考虑找到所有最终可能的 gcd⁡\gcd,随便找一个数 xx,由于最多 +t/−t+t/-t,因此这个 xx 最终只能在 [ai−t,ai+t][a_i-t, a_i+t] 这个范围内,因此 gcd⁡\gcd 也只能在这个范围内的数的质因数中。

我们对这个范围做区间筛,即可找到所有的质因数。具体而言,设这个区间为 [l,r][l,r],则可能的 gcd⁡\gcd(即所有质因数)包括:

  1. 2∼r2 \sim \sqrt r 内的所有质数,称为小质数。
  2. 对于 x∈[l,r]x \in [l,r],xx 超过 r\sqrt r 的质因数,而这个质因数最多只有一个,称为大质数。

把这些可能的 gcd⁡\gcd 全部暴力 check,check 的时候剪枝就行了。

看起来复杂度不太对,但是实际上长得很像答案的 gcd⁡\gcd 最多只有 1212 个,其中 1010 个是小质数,22 个是相邻的大质数。

const int N = 2e5 + 7;
const ll M = 2e6;
int n;
bool v[M|1];
ll a[N];
vector<ll> p, q;

int main() {
	for (ll i = 2; i <= M; i++) {
		if (v[i]) continue;
		p.pb(i);
		for (ll j = i * i; j <= M; j += i) v[j] = 1;
	}
	q = p;
	rd(n), rda(a, n);
	sort(a + 1, a + n + 1);
	ll d = a[1];
	for (int i = 2; i <= n; i++) d = __gcd(d, a[i]);
	if (d != 1) return print(0), 0;
	ll t = 0;
	for (int i = 1; i <= n; i++) t += a[i] & 1;
	ll l = max(M + 1, a[n] - t), r = a[n] + t;
	map<ll, ll> w;
	for (ll i = l; i <= r; i++) w[i] = i; 
	for (ll x : p) 
		for (ll i = l / x * x; i <= r; i += x)
			if (i >= l) while (!(w[i] % x)) w[i] /= x;
	for (ll i = l; i <= r; i++)
		if (w[i] > M) q.pb(w[i]);
	for (ll x : q) {
		if (x == 2) continue;
		ll o = 0;
		for (int i = 1; i <= n; i++) {
			o += min(a[i] < x ? x : a[i] % x, x - a[i] % x);
			if (o >= t) break;
		}
		t = min(t, o);
	}
	print(t);
	return 0;
}

Kuroni and Antihype

题目太神仙了,先咕咕咕着。

Kuroni the Private Tutor

题目太神仙了,先咕咕咕着。

标签:交互题、前缀和/差分、数论、构造、贪心

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…