Codeforces Round (2021-01-04)

게시일: 2021-01-03 16:00 1571자 8 min read 직접 씀 모든 글자를 제가 직접 썼어요.

이 게시물은 아직 한국어 번역이 없으므로 원문으로 표시돼요
Codeforces Round #693 (Div. 3) A. Cards for Friends 题目链接 有一张 $w\times h$ 的纸。如果一条边是偶数,就可以沿这个方向对半切。问能不能切出至少 $n$ 张纸。 宽和高每多一个因子 2,能切的份数就翻倍。分别统计出 和 ,最多就是 $2^{c1+c2}$ 份,再和 $n$ 比较就行。...

Codeforces Round #693 (Div. 3)

A. Cards for Friends

题目链接

题意

有一张 w×hw\times h 的纸。如果一条边是偶数,就可以沿这个方向对半切。问能不能切出至少 nn 张纸。

思路

宽和高每多一个因子 2,能切的份数就翻倍。分别统计出 c1 和 c2,最多就是 2c1+c22^{c_1+c_2} 份,再和 nn 比较就行。

两个方向都切到奇数以后就不能再切了。题目只要求至少 nn 份,不用考虑怎么恰好凑出 nn。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

int t, w, h, n;
int c1, c2;

int main() {
	cin >> t;
	while(t--) {
		cin >> w >> h >> n;
		c1 = 0, c2 = 0;
		while(!(w & 1)) w >>= 1, c1++;
		while(!(h & 1)) h >>= 1, c2++;
		// cout << c1 << " " << c1 << endl;
		if((1 << c1) * (1 << c2) >= n) cout << "YES" << endl;
		else cout << "NO" << endl;
	}
	return 0;
}

B. Fair Division

题目链接

题意

每颗糖的重量是 1 或 2,不能切开,问能不能分成总重量相同的两份。

思路

先数出 1 和 2 各有多少颗,记作 s1、s2。

s1 是奇数,总重量就是奇数,肯定不行。两种糖都是偶数个,分别平分就行。如果 2 是奇数个,就拿两颗 1 去和一颗 2 配平,所以这时还需要 s1 >= 2。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

int t, a[107], n;

int main() {
	cin >> t;
	while(t--) {
		cin >> n;
		int s1 = 0, s2 = 0;
		for(int i = 1; i <= n; i++) cin >> a[i];
		for(int i = 1; i <= n; i++) s1 += (a[i] == 1), s2 += (a[i] == 2);
		if((!(s1 & 1) && !(s2 & 1)) || (!(s1 & 1) && (s2 & 1) && s1 >= 2)) {
			cout << "YES" << endl;
		}
		else cout << "NO" << endl;
	}
	return 0;
}

C. Long Jumps

题目链接

题意

可以从数组里任意一个位置开始。每次拿到 aia_i 分,再跳到 i+aii+a_i,跳出数组结束。问从哪里开始能拿到最多的分。

思路

从一个位置出发以后,后面怎么跳就固定了。s[i] 表示从 ii 出发的总分,直接记忆化:

si=ai+si+ai.s_i=a_i+s_{i+a_i}.

跳出数组时返回 0。因为 aia_i 都是正数,只会往右跳,不会绕成环;每个位置算一次,总共 O(n)O(n)。

已经缓存的位置也不用单独作为起点再算:它是在前面某条路径上算出来的,而那条路径多拿了前面几步的分,总分只会更大。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

const int maxn = 2e5 + 17;

int n, t, a[maxn], s[maxn], ans;

int sv(int p) {
	if(p > n) return 0;
	if(!s[p]) s[p] = a[p] + sv(p + a[p]);
	return s[p];
}

int main() {
	cin >> t;
	while(t--) {
		cin >> n;
		for(int i = 1; i <= n; i++) cin >> a[i];
		for(int i = 1; i <= n; i++) s[i] = 0;
		ans = 0;
		for(int i = 1; i <= n; i++) if(!s[i]) {
			ans = max(ans, sv(i));
		}
		cout << ans << endl;
	}
	return 0;
}

D. Even-Odd Game

题目链接

题意

Alice 和 Bob 轮流拿走一个数,Alice 先手。Alice 拿偶数才加分,Bob 拿奇数才加分,拿到另一种数也会把它移走。问双方最优时谁赢。

思路

按从大到小的顺序拿就行。大数如果能给自己加分,就直接拿;如果只能给对方加分,拿走它也能阻止对方得分,不能只盯着自己的奇偶性选。

排完序以后交替取数,符合当前这一方的奇偶性就加分,否则跳过。最后比较两边的总分,注意总分用 long long。

时间主要在排序,是 O(nlog⁡n)O(n\log n)。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

typedef long long ll;
const int maxn = 2e5 + 17;

bool cmp(int x, int y) {
	return x > y;
}

int n, a[maxn], t;
ll s[2];

int main() {
	cin >> t;
	while(t--) {
		cin >> n;
		for(int i = 1; i <= n; i++) cin >> a[i];
		sort(a + 1, a + 1 + n, cmp);
		s[1] = 0, s[0] = 0;
		int c = 0;
		for(int i = 1; i <= n; i++) {
			if((a[i] & 1) == c) s[c] += 1ll * a[i];
			c = 1 - c;
		}
		if(s[0] > s[1]) puts("Alice");
		if(s[0] == s[1]) puts("Tie");
		if(s[0] < s[1]) puts("Bob");
	}
	return 0;
}

E. Correct Placement

题目链接

题意

每个人看作一个矩形,可以旋转 90 度。对每个人,找另一个高和宽都严格更小、能放到他前面的矩形,输出编号,没有就输出 -1。每个人的答案独立计算。

思路

旋转这里不用分两个方向扫。先把每个矩形的两条边排一下,统一让 h 是短边、w 是长边,就只需要同时满足短边更短、长边也更短。

再按短边排序,维护前面矩形里最小的长边。如果这个最小值还比当前长边小,就找到了答案;连它都不够小,其他候选也不行。

同一短边的一组要先全部查询,再更新最小值,不然相等的短边也会被拿来当答案。两个条件都是严格小于,不能只判断其中一条边和 max(h,w) 的关系。

比如 (1,10)(1,10) 和 (2,2)(2,2),后者的长边虽然更小,短边却放不进去,不能算答案。

这样还是排序加扫描,时间 O(nlog⁡n)O(n\log n),最后按原编号输出。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;

const int maxn = 2e5 + 17;
int t, n;
struct frd {
    int h, w, id, ans;
} f[maxn];

bool cmp1(frd x, frd y) { return x.h < y.h; }
bool cmp3(frd x, frd y) { return x.id < y.id; }

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> t;
    while(t--) {
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> f[i].h >> f[i].w;
            if(f[i].h > f[i].w) swap(f[i].h, f[i].w);
            f[i].id = i;
            f[i].ans = -1;
        }
        sort(f + 1, f + n + 1, cmp1);
        int best = 0;
        for(int i = 1, j; i <= n; i = j + 1) {
            j = i;
            while(j + 1 <= n && f[j + 1].h == f[i].h) j++;
            // 先查询整组;best 只来自短边严格更小的组。
            for(int k = i; k <= j; k++) {
                if(best && f[best].w < f[k].w) f[k].ans = f[best].id;
            }
            // 查询完才更新,避免相同短边互相当答案。
            for(int k = i; k <= j; k++) {
                if(!best || f[k].w < f[best].w) best = k;
            }
        }
        sort(f + 1, f + n + 1, cmp3);
        for(int i = 1; i <= n; i++) cout << f[i].ans << (i == n ? '\n' : ' ');
    }
    return 0;
}

집필 직접 씀

랜덤 게시물

© 2026 Zhiheng Wang @traceonsnow
Powered by theme astro-koharu · Inspired by Shoka
불러오는 중...